Architecture of
MLP
A Multilayer Perceptron (MLP) is generalized from the perceptron by
adding one or more hidden layers between the input and output layers.
That is, an MLP has three major components:
- An input layer
- One or more hidden layers
- An output layer
Each layer consists of multiple neurons (nodes)
connected via weights, with nonlinear activation functions that capture
complex relationships between features and the target, thereby improving
the algorithm’s performance.
If an MLP has only one hidden layer, this MLP is usually called
shallow neural network. Deep neural
networks have two or more hidden layers.
In the subsequent sections, we will briefly introduce the theory of
neural networks with an emphasis on shallow networks. Some case studies
will also be included to demonstrate the application of neural network
models in regression and classification.
Mathematics of
MLP
For illustration, we use the following one-hidden-layer MLP
regression to explain the mathematical foundations of the algorithm. The
objective is to predict the target variable \(y\) using input features \(\{x_1, x_2, \cdots, x_n \}\). If activation
functions \(\phi_1(\cdot)\) and \(\phi(\cdot)\) are correctly specified, then
the target can be expressed as a composition of weights through function
composition.

In essence, MLP is a problem of approximating a multi-variable
function. Assume that there are \(n\)
features in the scenario of the above one-hidden-layer MLP, the
relationship between the target variable \(y\) is characterized by \(y = f(x_1, x_2, \cdots, x_n: \mathbf{w})\),
where \(f(\cdot)\) is unknown and \(\mathbf{w}\) is the vector of unknown
parameters. That is, in the above MLP, the target can be expressed
as
\[
y = f(x_1, x_2, \cdots, x_n: \mathbf{w}) = \phi_2[\phi_1(x_1, x_2,
\cdots, x_n: \mathbf{w})].
\]
Note that \(f(\cdot)\) is an arbitrary function \(f: \mathbb{R}^n \rightarrow
\mathbb{R}\). For a given data set \(\{(x_{1i}, x_{2i}, \cdots,
x_{ni})\}_{i=1}^n\) and estimated \(\hat{\mathbf{w}}\), depending on
applications, the loss function is defined respectively by
- Continuous Regression (MSE)
\[
\mathcal{L} = \frac{1}{N} \sum_{c=1}^C (y_i - \hat{y}_i)^2
\]
- Discrete Classification (Cross-entropy)
\[
\mathcal{L} = - \sum_{c=1}^C y_c \log(\hat{y}_c)
\] for \(C\) classes. Using the
gradient decent method, we can express the weight updating formula in
the following
\[
\mathbf{W}^{(j+1)} \leftarrow \mathbf{W}^{(j)} - \eta \frac{\partial
\mathcal{L}}{\partial \mathbf{W}}\Big|_{\mathbf{W}=\mathbf{W}^{(j)}}
\] where \(\eta\) is the
hyperparameter of learning rate.
Universal
Approximation Theorem
The Universal Approximation Theorem is a fundamental
result in the theory of artificial neural networks, stating that a
feedforward neural network with a single hidden layer containing a
finite number of neurons can approximate continuous functions on compact
subsets of \(\mathbb{R}^n\) under mild
conditions on the activation function. The formal statement is given in
the following:
Cybenko’s Theorem (1989): Let \(\phi(\cdot)\) be a continuous, non-constant
activation function (e.g., sigmoid). Given any continuous function \(f: [0,1]^n \rightarrow \mathbb{R}\) and
\(\epsilon > 0\), there exists a
single-hidden-layer MLP (multilayer perceptron) with
finitely many hidden nodes (neurons), denoted by \(N(\mathbf{x})\), such that
\[
\sup_{\mathbf{x}\in [0,1]^n} \big|f(\mathbf{x}) - N(\mathbf{x}) \big|
< \epsilon.
\]
The above analysis assumes the support of the multivariable function
is \([0,1]^n\). This can be easily
generalized to any function with general finite support \([a-1, b_1]\times [a_2, b_2] \times
\cdots\times[a_k, b_k]\) via a linear transformation. For
example, assume \(f_k(x_k)\) has
support \([a_k, b_k]\), we can use
linear transformation \(y_k = (x_k - a_k)/(b_k
- a_k)\) which implies that \(y_k \in
[0,1]\).
This explains why a one-hidden-layer perceptron can
approximate any continuous function defined on a finite support using
finitely many nodes in the hidden layer. For functions with infinite
support, we can first approximate them on a finite domain and then apply
the universal approximation theorem to achieve the
desired result.
Recall that, the objective of regression analysis is to estimate the
unknown regression function based on a sample taken
from the underlying population and then use the
estimated regression function to
- assess the relationship between feature variables and the target
variable;
- predict the value of the target variable based on the values of the
input feature variables.
The universal approximation theorem for shallow
networks guarantees their capability as a function
approximator, making it a natural theoretical foundation for
neural network regression methods.
Some Graphical
Demonstrations
This subsection uses a simulation approach to demonstrate why MLP is
also a non-classical family of algorithmic regression models. We take a
set of points on the surface of a hypothesized elliptic paraboloid
regression surface and then use MLP to estimate the hypothetical
elliptic paraboloid model. The elliptic paraboloid used to generate data
is given by
\[
z = x_1^2 + x_2^2
\] The following figure shows the hypothetical regression
surface, along with the data points used to estimate it.
#library(plotly)
# Define the elliptic paraboloid function: z = (x^2 / a^2) + (y^2 / b^2)
# Create a grid of x and y values
x <- seq(-2, 2, length =200)
y <- seq(-2, 2, length =200)
z <- outer(x, y, function(x, y) (x^2 + y^2 ))
## sample points on the surface to define a data set
x.sim <- sample(x)
y.sim <- sample(y)
z.sample <- (x.sim^2 + y.sim^2) + rnorm(200, 0, 0.5)
# Plot margins
m <- list( l = 50, r = 50, b = 100, t = 100, pad = 4)
# Create the 3D surface plot
plot_ly(x = x, y = y, z = z, type = "surface") %>% # the surface
add_markers(x = x.sim, # sample points near the surface
y = y.sim,
z = z.sample,
marker = list(size = 3, color = "darkred")) %>%
layout(margin = m,
title = "Elliptic Paraboloid: Hypothesized Model for Data Generation",
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")
)
)
In practice, we only observe data points from an unknown surface. We
therefore remove the hypothetical regression surface and visualize just
the data points to reveal the data cloud’s structure.
plot_ly() %>%
add_markers(x = x.sim, y = y.sim, z = z.sample,
marker = list(size = 3, color = "darkred")) %>%
layout( margin = m,
title = "Sample points from the elliptic paraboloid",
scene = list( xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")
)
)
There is a nonlinear relationship between the target variable (\(z\)) and feature variables (\(x_1\) and \(x_2\)). In the following figures, we
compare the performance of three models fitted to the same dataset: a
single perceptron, a one-hidden-layer perceptron, and a two-hidden-layer
perceptron. We then plot their corresponding estimated regression
surfaces to visualize the goodness of fit.
#library(neuralnet)
#library(ggplot2)
#library(plotly) # For 3D visualization
# Generate training data
#set.seed(123)
#x <- seq(-2, 2, length =20)
#y <- seq(-2, 2, length =20)
#z_train <- x_train^2 + y_train^2 # + rnorm(500, sd = 0.1) # Added noise
## The working data set: we call it the training data set
train.data <- data.frame(x = x.sim, y = y.sim, z = z.sample)
### no hidden layer
hidden0.0 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(0),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
### single hidden layer
hidden1.8 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(8),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
# Train neural network with 2 hidden layers (8 and 4 neurons)
hidden2.8.4 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(8,4),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
# Create test grid to draw the Predicted surface based on different NN models
x.test <- seq(-2, 2, length.out = 200)
y.test <- seq(-2, 2, length.out = 200)
test.grid <- expand.grid(x = x.test, y = y.test)
z.test0.0 <- matrix(predict(hidden0.0, test.grid),ncol=200)
z.test1.8 <- matrix(predict(hidden1.8, test.grid),ncol=200)
z.test2.8.4 <- matrix(predict(hidden2.8.4, test.grid),ncol=200)
The following figure shows the predicted surface from the perceptron
model, which produces a linear hyperplane, along with the data points
used to estimate the regression surface. The perceptron fits the data
poorly.
## set up plot margin
m <- list(l = 50,r = 50, b = 100, t = 100, pad = 4)
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test0.0, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Perceptron Model (with no hidden layer)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
Next, we fit a one-hidden-layer perceptron with 8
nodes to the same dataset. The following figure shows both the
estimated regression surface and the data points. We observe that the
predicted surface closely approximates the hypothesized
regression surface. Note that the number of nodes in the hidden
layer is a tunable hyperparameter that can be optimized to improve model
performance.
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test1.8, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Estimated One-hidden-layer Perceptron (with 8 nodes)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
Finally, we examine a two-hidden-layer perceptron architecture, with
8 nodes in the first hidden layer and 4 nodes in the second hidden
layer, trained on the same data set.
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test2.8.4, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Estimated Two-hidden-layer Perceptron (with 8 and 4 nodes)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
The figure above shows both the estimated prediction surface and the
original data points. We observe a significant discrepancy between the
estimated regression surface and the
hypothesized (true) regression surface. Upon closer
examination, we find that the estimated regression surface yields small
residuals, as the data points lie close to the fitted surface. This
suggests potential overfitting in the two-hidden-layer
perceptron model.
MLP Modeling
Process
MLPs can learn complex, non-linear relationships due to their
multiple layers of interconnected neurons. However, designing and
training an effective MLP requires careful consideration of data
preprocessing, architecture selection, optimization techniques, and
other strategies.
Data
Preprocessing
Before training an MLP, the input data must be properly prepared to
make sure the MLP learns efficiently.
Feature
Scaling
MLPs are sensitive to feature scales, so normalization (e.g., Min-Max
scaling) or standardization (e.g., Z-score normalization) is typically
applied to ensure stable gradient descent optimization.
Whether the target variable must be rescaled in a neural network
depends on the context, but it is often strongly recommended for optimal
performance.
The scaling of the target is necessary when
Output Activation Constraints: If using a
sigmoid (bounded to [0, 1]) or tanh (bounded to [-1, 1]) output
activation, the target variable must be scaled to match these
ranges.
Loss Function Sensitivity: Loss functions like
MSE (Mean Squared Error) are sensitive to scale. Large target values can
lead to unstable gradients or slow convergence.
Regularization Terms: \(L_1\)/\(L_2\) regularization penalizes large
weights. Unscaled targets may force the network to learn
disproportionately large weights to compensate.
While not always mandatory, rescaling the target
variable typically improves training
stability, speed, and model performance—especially for bounded
activations or large-value ranges. Always validate with your specific
architecture and data.
Handling Missing
Data
Handling missing values in Multilayer Perceptrons
(MLPs) requires careful consideration, as neural networks
typically expect complete input data.
All imputation methods discussed in the previous section can be used
to preprocess data with missing values for MLP modeling. The general
recommendation is to use stochastic multiple imputation techniques, such
as MICE (Multiple Imputation by Chained Equations) and other random
replacement methods that make use of the probability distribution of the
underlying feature variables.
Categorical
Encoding
Categorical encoding transforms non-numeric
variables into numerical representations for machine learning.
For nominal data (no inherent order), common
methods include one-hot encoding, which creates binary columns for each
category (ideal for <15 categories), and target encoding, which
replaces categories with the mean of the target variable (better for
high-cardinality features but requires careful validation to avoid
leakage).
For ordinal data (natural order), ordinal
encoding assigns integers while preserving the logical sequence (e.g.,
Small=1, Medium=2).
High-cardinality features (e.g., ZIP codes) may
use frequency encoding (replacing categories with their occurrence
counts) or embedding layers in neural networks to capture complex
relationships.
The choice depends on the model type and data characteristics:
Tree-based models (e.g., Random Forests) often
handle label encoding well.
Linear models and neural networks typically
require one-hot or target encoding.
Cross-validation is essential to evaluate the impact of encoding
strategies on model performance.
Data Splitting
Proper data splitting is crucial for training Multilayer
Perceptrons (MLPs) to ensure robust model evaluation and
prevent overfitting. For cross-sectional data, a random splitting is
required:
Two-way splitting for training and testing data sets. The
training and test data ratio is usually 70%-30% or 80%-20%.
Nested random splitting for cross-validation
involves two layers of data partitioning: an outer loop for performance
evaluation and an inner loop for hyperparameter tuning. This approach
ensures unbiased model assessment by preventing data leakage between the
tuning and evaluation phases.
There are other random data splitting methods based on the data
structures that require special design so that the randomly split data
retain the same probability structure of the data set.
MLP Architecture
Desing
In neural networks, MLP (Multilayer Perceptron) size and depth refer
to its architecture, particularly the number of neurons (size) and the
number of hidden layers (depth). These factors significantly impact the
model’s capacity, training dynamics, and generalization ability.
- Shallow networks (1-2 hidden layers) may suffice
for simple tasks. Shallow networks
- can approximate any continuous function (Universal Approximation
Theorem).
- often sufficient for simple tasks (e.g., small tabular
datasets).
- are less prone to overfitting but may underfit complex data.
- Deep networks can model complex patterns but risk
overfitting and increased computational cost. Deep
networks are
- better at learning hierarchical representations.
- useful for high-dimensional data (e.g., images, NLP after feature
extraction).
- more computationally expensive and prone to overfitting (requires
regularization like Dropout, L2).
- MLP Size (Number of Neurons per Layer) is also
impactful on the model performance.
- Wider Layers (more neurons) increase model capacity, allowing more
complex function approximation. It can lead to overfitting if not
regularized and use more computation resources (more parameters).
- Narrower Layers (fewer neurons) are less expressive but faster to
train but may lead to underfitting complex patterns.
- Choosing Depth & Size is based on the following
general recommendations
- Start with 1-3 hidden layers and adjust based on performance.
- Use similar or decreasing layer sizes.
- Wider layers early can help feature extraction; deeper networks
refine abstractions.
Activation
Functions
Activation functions serve distinct purposes in
hidden and output layers. In hidden layers, they introduce nonlinear
transformations to capture complex patterns in the data. The output
layer’s activation function is chosen specifically to produce
predictions compatible with the target variable’s characteristics.
Hidden Layer
Activation
There are several activation functions for the hidden layer. The ReLU
(Rectified Linear Unit) is widely used due to its computational
efficiency and is the default for many MLP libraries. The following
table outlines some of the commonly used activation functions for hidden
layers.
| ReLU |
Default choice |
Fast, avoids vanishing gradient (for x > 0) |
“Dying ReLU” (dead neurons) |
First try in most MLPs |
| Leaky ReLU |
Deep networks |
Fixes dying ReLU |
Slightly slower than ReLU |
Use if ReLU fails |
| GELU |
Transformers, deep learning |
Smooth, better gradient flow |
More compute-heavy |
Best for modern deep nets (e.g., BERT, GPT) |
| Swish |
Experimental alternative |
Can outperform ReLU |
Slower than ReLU |
Try if tuning performance |
| SELU |
Self-normalizing networks |
No BatchNorm needed |
Requires careful initialization |
Use only in specific architectures |
Avoid using Sigmoid/Tanh in hidden layers
that cause vanishing gradients in deep networks.
Output Layer
Activation
Depending on the type of the target response, the commonly used
activation functions are summarized in the following table.
| Regression (unbounded) |
Linear (no activation) |
Outputs any real number |
Predicting house prices |
| Regression (0 to 1) |
Sigmoid |
Bounds output to [0, 1] |
Predicting probabilities |
| Binary Classification |
Sigmoid |
Outputs probability (0 or 1) |
Spam detection |
| Multiclass Classification |
Softmax |
Probabilities sum to 1 |
MNIST digit classification |
| Multilabel Classification |
Sigmoid (per class) |
Independent probabilities |
Image tagging |
Never use ReLU in the output layer unless
outputs must be ≥0, e.g., count prediction like Poisson
regression).
Guideline for
Hidden and Output Layers
The following table provides a guideline for choosing appropriate
activation functions in MLP.
| General MLP |
ReLU |
Task-dependent (see above) |
| Deep Network |
GELU/Leaky ReLU |
Task-dependent |
| Transformer Model |
GELU |
Softmax/Sigmoid |
| Self-Normalizing Net |
SELU |
Task-dependent |
Training,
Optimization
Effective training requires proper optimization techniques and
hyperparameter tuning.
- Loss Function Selection is dependent on the
applications:
- Classification: Cross-entropy loss
- Regression: Mean Squared Error (MSE) or Mean
Absolute Error (MAE)
- Optimizer Choice is also dependent on the
applications and the required computational resources for the
applications.
- Stochastic Gradient Descent (SGD): Basic but requires careful
learning rate tuning.
- Adaptive Optimizers (Adam, RMSprop): Automatically adjust learning
rates, often leading to faster convergence. A too-high learning rate
causes instability, while a too-low rate slows convergence.
- Hyperparameter Tuning Methods like grid search,
random search, or Bayesian optimization help find optimal configurations
efficiently.
MLP Regression
This section focuses on implementing MLP regression using the Boston
Housing dataset, along with the R neuralnet library and
other supporting libraries for data preparation and visualization. We
will follow the basic steps outlined in the previous section. We will
fit one-hidden-layer and two-hidden-layer MLP to predict the median
house value.
We use min-max scaling method for all numerical
variables. All features are numerical, no categorical encoding is
needed. We use random splitting (70-30) to create training and testing
data sets.
# Load necessary libraries
# library(neuralnet)
# library(MASS) # For Boston Housing dataset
# library(ggplot2) # For visualization
# library(caret) # Only for data splitting (we won't use its modeling functions)
# Load Boston Housing dataset
data(Boston)
# Check structure and summary
#str(Boston)
#summary(Boston)
# Feature scaling - normalize all variables to [0,1] range
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
boston.scaled <- as.data.frame(lapply(Boston, normalize))
# Set seed for reproducibility
set.seed(123)
N <- length(boston.scaled$medv)
# Create train-test split (70-30)
train.reg.index <- sample(1:N, floor(0.7*N), replace = FALSE)
train.reg.data <- boston.scaled[train.reg.index, ]
test.reg.data <- boston.scaled[-train.reg.index, ]
# Check dimensions
#dim(train_data)
#dim(test_data)
One-hidden-layer
Perceptron
We first build a single hidden layer perceptron model. We will tune
three hyperparameters: the number of nodes in the hidden layer, learning
rate, and activation function using grid search. The performance metric
used to select the optimal combination of values of hyperparameters is
RMSE.
# Define grid of hyperparameters
hyper.grid.reg <- expand.grid(
layer1 = c(5, 10, 15),
learning.rate = c(0.01, 0.1),
activation = c("logistic", "tanh")
)
# Initialize results storage
rmse = NULL
#layer1 = NULL
#learningrate = NULL
#activation = NULL
best.reg.rmse <- Inf
best.reg.model <- NULL
# Perform grid search
for(i in 1:nrow(hyper.grid.reg)) {
# Get current configuration
layer <- hyper.grid.reg$layer1[i]
lr <- hyper.grid.reg$learning.rate[i]
act <- hyper.grid.reg$activation[i]
# Train model
set.seed(123)
model.reg <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = layer,
act.fct = act,
linear.output = TRUE, # For regression
learningrate = lr,
algorithm = "rprop+",
stepmax = 1e5 )
# Make predictions
preds.reg <- predict(model.reg, test.reg.data[, -ncol(test.reg.data)])
# Calculate RMSE
rmse.reg <- sqrt(mean((preds.reg - test.reg.data$medv)^2))
# Store results
rmse[i] = rmse.reg
# Update best model
if(rmse.reg < best.reg.rmse) {
best.reg.rmse <- rmse.reg
best.reg.model <- model.reg
best.reg.params <- hyper.grid.reg[i, ]
}
}
results.regNN <- hyper.grid.reg
results.regNN$rmse <- rmse
# View results sorted by RMSE
pander(results.regNN[order(results.regNN$rmse), ][1,])
| 3 |
15 |
0.01 |
logistic |
0.08035 |
With the above combination of optimal hyperparameter values, we train
the final single hidden layer perceptron model.
# Train the final model with best parameters
final.reg.model <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = best.reg.params$layer1,
act.fct = best.reg.params$activation,
linear.output = TRUE,
learningrate = best.reg.params$learning_rate,
algorithm = "rprop+",
stepmax = 1e5
)
# Plot the neural network
#plot(final.reg.model)

The above NN plot shows the architecture of the final
one-hidden-layer perceptron model. Next, we will use it to make
predictions.
# Make predictions on test set
pred.NN1 <- predict(final.reg.model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
rmse.NN1 <- sqrt(mean((pred.NN1 - test.reg.data$medv)^2))
mae.NN1 <- mean(abs(pred.NN1 - test.reg.data$medv))
r.squared.NN1 <- cor(pred.NN1 , test.reg.data$medv)^2
# cat("Performance Metrics:\n")
# cat("RMSE:", rmse, "\n")
# cat("MAE:", mae, "\n")
# cat("R-squared:", r_squared, "\n")
# Plot predictions vs actual
plot.NN1.data <- data.frame(
Actual = test.reg.data$medv,
Predicted = pred.NN1
)
ggplot(plot.NN1.data, aes(x = Actual, y = Predicted)) +
geom_point() +
geom_abline(intercept = 0, slope = 1, color = "darkred") +
annotate("text", x=0.85, y=.2,
label=paste("R.sq =", round(r.squared.NN1,4)), color="blue") +
annotate("text", x=0.85, y=.13,
label=paste("RMSE =", round(rmse.NN1,4)), color="blue") +
annotate("text", x=0.85, y=.06,
label=paste(" MAE =", round(mae.NN1,4)), color="blue") +
ggtitle("Actual vs Predicted Values") +
theme_minimal()

The figure above demonstrates a strong correlation between the
predicted target values and the scaled target values. Both the mean
squared error (MSE) and mean absolute error (MAE) metrics are displayed
on the plot. Next, we fit a classical linear regression model to the
scaled data and perform a comparative analysis between this baseline
linear regression and our neural network model.
# library(ggplot2)
# Train linear regression model
lm.model <- lm(medv ~ ., data = test.reg.data)
# Make predictions
lm.predictions <- predict(lm.model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
lm.rmse <- sqrt(mean((lm.predictions - test.reg.data$medv)^2))
lm.mae <- mean(abs(lm.predictions - test.reg.data$medv))
lm.r.squared <- cor(lm.predictions, test.reg.data$medv)^2
## improvements
RMSE.imp <- round((lm.rmse - rmse.NN1)/lm.rmse * 100,2)
MAE.imp <- round((lm.mae - mae.NN1)/lm.mae * 100, 2)
Rsq.imp <- round((r.squared.NN1 - lm.r.squared)/lm.r.squared * 100,2)
##
Performance.table <- data.frame(
LM = c(lm.rmse, lm.mae, lm.r.squared),
NN.1 = c(rmse.NN1, mae.NN1, r.squared.NN1),
Improvement.percentage = c(RMSE.imp, MAE.imp, Rsq.imp)
)
rownames(Performance.table) <- c("RMSE", "MAE", "R.square")
pander(Performance.table)
| RMSE |
0.09682 |
0.08986 |
7.19 |
| MAE |
0.07097 |
0.0565 |
20.39 |
| R.square |
0.7858 |
0.8299 |
5.61 |
# Plot both predictions
comparison.data <- data.frame(
Actual = test.reg.data$medv,
MLP = pred.NN1,
Linear = lm.predictions
)
ggplot(comparison.data) +
geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
geom_abline(intercept = 0, slope = 1, color = "black") +
scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
labs(title = "Model Comparison: Actual vs Predicted",
x = "Actual Values",
y = "Predicted Values",
color = "Model Type") +
theme(
plot.margin = ggplot2::margin(40, 20, 20, 20, unit = "pt"),
plot.title = element_text(hjust = 0.5,
lineheight = 1.1,
vjust = 10)
)

The above scatter plot of the true target values and predicted values
based on the two models also shows that the one-hidden-perceptron model
outperforms the classic linear regression model.
Two-hidden-layer
Perceptron
This subsection explores whether a two-hidden-layer perception that
has more complex architecture could improve the performance.
The model-building process is identical to the previous
one-hidden-layer perceptron model. We will not detail the steps as we
did in the previous section.
# Define grid of hyperparameters
hyper.grid.NN2 <- expand.grid(
layer1 = c(5, 10, 15),
layer2 = c(0, 3, 5), # 0 means no second layer
learning.rate = c(0.01, 0.1),
activation = c("logistic", "tanh")
)
# Initialize results in storage
results <- data.frame()
best.rmse <- Inf
best.model <- NULL
# Perform grid search
for(i in 1:nrow(hyper.grid.NN2)) {
# Get current configuration
layer1 <- hyper.grid.NN2$layer1[i]
layer2 <- hyper.grid.NN2$layer2[i]
lr <- hyper.grid.NN2$learning.rate[i]
act <- hyper.grid.NN2$activation[i]
# Create hidden layers vector
if(layer2 == 0) {
hidden.layers <- c(layer1)
} else {
hidden.layers <- c(layer1, layer2)
}
# Train model
set.seed(123)
model.NN2 <- tryCatch({
neuralnet(
medv ~ .,
data = train.reg.data,
hidden = hidden.layers,
act.fct = act,
linear.output = TRUE, # For regression
learningrate = lr,
algorithm = "rprop+",
stepmax = 1e5
)
}, error = function(e) NULL)
if(!is.null(model.NN2)) {
# Make predictions
preds <- predict(model.NN2, test.reg.data[, -ncol(test.reg.data)])
# Calculate RMSE
rmse <- sqrt(mean((preds - test.reg.data$medv)^2))
# Store results
results <- rbind(results, data.frame(
layer1 = layer1,
layer2 = layer2,
learning_rate = lr,
activation = act,
rmse = rmse
))
# Update best model
if(rmse < best.rmse) {
best.rmse <- rmse
best.model <- model.NN2
best.params <- hyper.grid.NN2[i, ]
}
}
}
# View results sorted by RMSE
results[order(results$rmse), ]
# Train the final model with best parameters
final.model.NN2 <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = if(best.params$layer2 == 0) {
c(best.params$layer1)} else {
c(best.params$layer1, best.params$layer2)},
act.fct = best.params$activation,
linear.output = TRUE,
learningrate = best.params$learning.rate,
algorithm = "rprop+",
stepmax = 1e5
)
# Plot the neural network
#plot(final.model.NN2)

# Make predictions on test set
pred.NN2 <- predict(final.model.NN2, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
rmse.NN2 <- sqrt(mean((pred.NN2 - test.reg.data$medv)^2))
mae.NN2 <- mean(abs(pred.NN2 - test.reg.data$medv))
r.squared.NN2 <- cor(pred.NN2 , test.reg.data$medv)^2
## vector of error metric
NN2 <- c(rmse.NN2, mae.NN2, r.squared.NN2)
# Plot predictions vs actual
plot.data.NN2 <- data.frame(
Actual = test.reg.data$medv,
Predicted = pred.NN2
)
ggplot(plot.data.NN2, aes(x = Actual, y = Predicted)) +
geom_point() +
geom_abline(intercept = 0, slope = 1, color = "red") +
ggtitle("Actual vs Predicted Values") +
theme_minimal()

# Train linear regression model
lm.model <- lm(medv ~ ., data = train.reg.data)
# Make predictions
lm.predictions <- predict(lm.model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
lm.rmse <- sqrt(mean((lm.predictions - test.reg.data$medv)^2))
lm.mae <- mean(abs(lm.predictions - test.reg.data$medv))
lm.r.squared <- cor(lm.predictions, test.reg.data$medv)^2
###
rmse.NN2.imp <- (lm.rmse - rmse.NN2)/lm.rmse*100
mae.NN2.imp <- (lm.mae - mae.NN2)/lm.mae * 100
Rsq.NN2.imp <- (r.squared.NN2 - lm.r.squared)/lm.r.squared * 100
NN2.improve <-c(rmse.NN2.imp, mae.NN2.imp, Rsq.NN2.imp)
perf.matrix <-data.frame(
LM = c(lm.rmse, lm.mae, lm.r.squared),
NN.1 = c(rmse.NN1, mae.NN1, r.squared.NN1),
NN.2 = c(rmse.NN2, mae.NN2, r.squared.NN2)
)
perf.matrix$NN1.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.1)/perf.matrix$LM,2)
perf.matrix$NN2.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.2)/perf.matrix$LM,2)
rownames(perf.matrix) <- c("RMSE", "MAE", "R.sq")
pander(perf.matrix)
| RMSE |
0.1067 |
0.08986 |
0.09108 |
15.81 |
14.67 |
| MAE |
0.07848 |
0.0565 |
0.05954 |
28.01 |
24.13 |
| R.sq |
0.7461 |
0.8299 |
0.8171 |
-11.23 |
-9.51 |
# Plot both predictions
comparison.data.NN2 <- data.frame(
Actual = test.reg.data$medv,
MLP = pred.NN2 ,
Linear = lm.predictions
)
ggplot(comparison.data.NN2) +
geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
geom_abline(intercept = 0, slope = 1, color = "black") +
scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
labs(title = "Model Comparison: Actual vs Predicted",
x = "Actual Values",
y = "Predicted Values",
color = "Model Type") +
theme_minimal()

MLP Classification
The modeling process for MLP classification is identical to that of
MLP regression. As we did in the previous section on MLP regression, we
will document the modeling process in detail.
The Pima Indian Diabetes dataset contains only
numerical feature variables. Therefore, we will apply min-max scaling to
all features and convert the target variable into a factor variable.
# Load necessary libraries
# library(neuralnet)
# library(pROC) # For ROC analysis
# library(ggplot2) # For visualization
# Load the Pima Indians Diabetes dataset
data("PimaIndiansDiabetes2", package = "mlbench")
## removing records with missing component; imputation should be considered in
## practical applications
diabetes.data <- na.omit(PimaIndiansDiabetes2)
# Feature scaling - normalize numeric variables to [0,1] range
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
# Apply normalization to all numeric columns
numeric.cols <- sapply(diabetes.data, is.numeric)
diabetes.data[numeric.cols] <- lapply(diabetes.data[numeric.cols], normalize)
# Encode the target variable (diabetes) as numeric (0/1)
diabetes.data$diabetes <- ifelse(diabetes.data$diabetes == "pos", 1, 0)
# Two-way data splitting: 70-30%
set.seed(123) # For reproducibility
sample.size.cls <- floor(0.70 * nrow(diabetes.data))
train.indices.cls <- sample(1:sample.size.cls, size = sample.size.cls, replace = FALSE)
train.data.cls <- diabetes.data[train.indices.cls, ]
test.data.cls <- diabetes.data[-train.indices.cls, ]
To simplify hyperparameter tuning and final model training with the
pre-selected MLP architecture for classification, we define a custom
function to determine the optimal number of nodes for both
single-hidden-layer and double-hidden-layer MLPs. The criterion for
selecting the optimal number of nodes is the area under the ROC curve
(AUC) as the evaluation metric, though accuracy based on the default 0.5
thresholds could alternatively be used. We choose the logistic
activation function while keeping all other hyperparameters at their
default values.
# Function to perform grid search for neuralnet
neuralnet.grid.search <- function(train.data, test.data, hidden.layers = 1) {
# Define the grid of hyperparameters
if (hidden.layers == 1) {
hidden.nodes <- c(2, 4, 6, 8, 10)
grid <- expand.grid(hidden = hidden.nodes)
} else {
hidden.nodes <- c(2, 4, 6)
grid <- expand.grid(hidden1 = hidden.nodes, hidden2 = hidden.nodes)
}
# Add columns to store results
grid$accuracy <- NA
grid$auc <- NA
# Formula for neural network
nn.formula <- as.formula(paste("diabetes ~",
paste(names(train.data)[!names(train.data) %in% "diabetes"],
collapse = " + ")))
# Perform grid search
for (i in 1:nrow(grid)) {
if (hidden.layers == 1) {
hidden <- c(grid$hidden[i])
} else {
hidden <- c(grid$hidden1[i], grid$hidden2[i])
}
# Train the model
nn.model <- neuralnet(
formula = nn.formula,
data = train.data,
hidden = hidden,
linear.output = FALSE, # For classification
act.fct = "logistic", # Sigmoid activation
stepmax = 1e6 # Increase max steps for convergence
)
# Make predictions
predictions <- predict(nn.model, test.data)
predicted.classes <- ifelse(predictions > 0.5, 1, 0)
# Calculate accuracy
accuracy <- mean(predicted.classes == test.data$diabetes)
# Calculate AUC
roc.obj <- roc(test.data$diabetes, predictions)
auc.value <- auc(roc.obj)
# Store results
grid$accuracy[i] <- accuracy
grid$auc[i] <- auc.value
}
return(grid)
}
The performance table of corresponding number of nodes in
one-hidden-layer MLP is given below.
# Perform grid search for single hidden layer
grid.results.1layer <- neuralnet.grid.search(train.data=train.data.cls,
test.data=test.data.cls,
hidden.layers = 1)
pander(grid.results.1layer)
| 2 |
0.8305 |
0.8569 |
| 4 |
0.822 |
0.8659 |
| 6 |
0.839 |
0.8509 |
| 8 |
0.8559 |
0.8941 |
| 10 |
0.822 |
0.873 |
The optimal number of nodes in the hidden layer is the corresponds to
the smallest AUC. Similarly, the performance table of two-hidden-layer
MLP is given below.
# Perform grid search for two hidden layers
grid.results.2layer <- neuralnet.grid.search(train.data=train.data.cls,
test.data=test.data.cls,
hidden.layers = 2)
pander(grid.results.2layer)
| 2 |
2 |
0.8305 |
0.856 |
| 4 |
2 |
0.822 |
0.8879 |
| 6 |
2 |
0.822 |
0.8742 |
| 2 |
4 |
0.8136 |
0.8166 |
| 4 |
4 |
0.7881 |
0.8318 |
| 6 |
4 |
0.8136 |
0.8979 |
| 2 |
6 |
0.8051 |
0.8956 |
| 4 |
6 |
0.7797 |
0.8552 |
| 6 |
6 |
0.8644 |
0.9087 |
One-hidden-layer MLP
We use the optimal number of nodes to fitthe onehidden-layer MLP to
the data anf obtain
# Formula for neural network
nn.formula <- as.formula(paste("diabetes ~",
paste(names(train.data.cls)[!names(train.data.cls) %in% "diabetes"],
collapse = " + ")))
# Train single hidden layer model (using best configuration from grid search)
best.1layer <- grid.results.1layer[which.max(grid.results.1layer$auc), ]
nn.1layer <- neuralnet(
formula = nn.formula,
data = train.data.cls,
hidden = best.1layer$hidden,
linear.output = FALSE,
act.fct = "logistic",
stepmax = 1e6
)
##
#plot(nn.1layer, main = paste("One-hidden-layer with", best.1layer$hidden, "Nodes"))

# Train two hidden layers model (using best configuration from grid search)
best.2layer <- grid.results.2layer[which.max(grid.results.2layer$auc), ]
nn.2layer <- neuralnet(
formula = nn.formula,
data = train.data.cls,
hidden = c(best.2layer$hidden1, best.2layer$hidden2),
linear.output = FALSE,
act.fct = "logistic",
stepmax = 1e6
)
##
#plot(nn.2layer)

In the two model plots above, the Error and
Steps values displayed at the bottom represent:
Steps: The number of training
iterations (epochs) completed during model optimization. Each
step corresponds to one complete forward/backward pass and weight update
cycle.
Errors: The training error
reflects the loss function value (typically SSE for regression or
cross-entropy for classification). The displayed Error
represents the final error value achieved when the optimization process
converges.
Next, we write a custom function to extract the performance metrics
to assess the global performance through ROC curves and the
corresponding areas under the ROC curves.
# Function to evaluate model performance
evaluate.model <- function(model, test.data) {
# Make predictions
predictions <- predict(model, test.data)
predicted.classes <- ifelse(predictions > 0.5, 1, 0)
# Calculate metrics
accuracy <- mean(predicted.classes == test.data$diabetes)
confusion.matrix <- table(Predicted = predicted.classes, Actual = test.data$diabetes)
roc.obj <- roc(test.data$diabetes, predictions)
auc.value <- auc(roc.obj)
return(list(
accuracy = accuracy,
confusion.matrix = confusion.matrix,
roc.obj = roc.obj,
auc = auc.value
))
}
# Evaluate single hidden layer model
perf.1layer <- evaluate.model(nn.1layer, test.data.cls)
#print(perf.1layer[c("accuracy", "confusion_matrix", "auc")])
# Evaluate two hidden layers model
perf.2layer <- evaluate.model(nn.2layer, test.data.cls)
#print(perf.2layer[c("accuracy", "confusion_matrix", "auc")])
We use classic logistic regression as the base model and compare it
with the two MLPs using ROC curves and their corresponding AUC values in
the following figure.
# Train logistic regression model (base model)
logit.model <- glm(diabetes ~ ., data = train.data.cls, family = binomial)
# Evaluate logistic regression model
logit.pred <- predict(logit.model, test.data.cls, type = "response")
logit.classes <- ifelse(logit.pred > 0.5, 1, 0)
logit.accuracy <- mean(logit.classes == test.data.cls$diabetes)
logit.roc <- roc(test.data.cls$diabetes, logit.pred)
logit.auc <- auc(logit.roc)
##
roc.1layer <- perf.1layer$roc.obj
roc.2layer <- perf.2layer$roc.obj
roc.logit <- logit.roc
## specificity and sensitivity
sen.1layer <- roc.1layer$sensitivities
spe.1layer <- roc.1layer$specificities
sen.2layer <- roc.2layer$sensitivities
spe.2layer <- roc.2layer$specificities
sen.logit <- roc.logit$sensitivities
spe.logit <- roc.logit$specificities
## AUC
auc.1layer <- roc.1layer$auc
auc.2layer <- roc.2layer$auc
auc.logit <- roc.logit$auc
## Plot ROC curves for comparison
par(pty = "s") # make a square plot to avaoid distortion
plot(1-spe.1layer, sen.1layer, type = "l", lty = 1,
col = "blue",
xlab = "1 - specificity",
ylab = "sensitvity",
main = "ROC Curve Comparison")
lines(1-spe.2layer, sen.2layer, lty = 1, col = "darkred")
lines(1-spe.logit, sen.logit, lty = 1, col = "darkgreen")
legend("bottomright",
legend = c(paste("1-layer MLP (AUC =", round(perf.1layer$auc, 3), ")"),
paste("2-layer MLP (AUC =", round(perf.2layer$auc, 3), ")"),
paste("Logistic Reg (AUC =", round(logit.auc, 3), ")")),
col = c("blue", "darkred", "darkgreen"),
lty = 1, cex = 0.7, bty = "n")

The ROC analysis shows that the classic logistic regression model
performs slightly better than the two MLPs. The two MLPs perform equally
well, even though the two-hidden-layer MLP has more parameters (weights)
and is more complex than the single-hidden-layer MLP. In general, a
single-hidden-layer MLP is recommended if an MLP is used for
classification.
MLP or Classical
Models?
We have discussed the MLP architecture and the process of
implementing MLPs alongside classic models. Case studies using the
Boston Housing Data demonstrate that MLP regression
outperforms classic linear regression, while the classic logistic
regression performs better than MLP classification in the Pima
Indian Diabetes dataset analysis. Next, we will present a
general comparison between these models.
MLP Classification vs. Logistic Regression
The comparison between multilayer perceptron (MLP) classifiers and
logistic regression highlights a fundamental trade-off between
flexibility and simplicity.
Logistic regression, as a linear classifier, performs well when
decision boundaries are approximately linear, offering high
interpretability, computational efficiency, and robustness to
overfitting—particularly with limited data.
MLPs, with their nonlinear activation functions, can model more
complex decision boundaries but require careful tuning of architecture
and regularization to avoid overfitting.
The added complexity of neural networks may not always be
justified for simpler classification tasks. However, MLPs remain
valuable when dealing with highly nonlinear data where logistic
regression’s linear assumptions fail.
MLP Regression vs. Classic Linear Regression
Similar to their classification counterparts, MLP regression models
and classical linear regression serve different purposes depending on
the nature of the data.
Linear regression is optimal when relationships between
predictors and the target variable are linear, providing interpretable
coefficients, fast training, and low risk of overfitting.
MLP regression, however, excels in capturing nonlinear and
interactive effects, making it suitable for more complex regression
tasks where linear models underperform.
The trade-off lies in computational cost, tuning complexity, and
the need for larger datasets to prevent overfitting. If
the underlying data structure is unknown, a practical approach is to
begin with linear regression as a baseline before considering MLPs if
residuals suggest unmodeled nonlinearity.
Single-Hidden-Layer vs. Two-Hidden-Layer MLPs
The comparison between single- and
two-hidden-layer MLPs underscores model complexity.
Deeper networks theoretically have greater
representational power. Case studies found that the
two-hidden-layer MLP did not outperform its simpler
counterpart, despite having more parameters. This is consistent with the
universal approximation theorem, which states that a single hidden layer
(with sufficient neurons) can approximate any continuous
function.
The additional layer introduced unnecessary complexity without
gains in accuracy, reinforcing that deeper architectures should only be
explored when simpler models prove inadequate—typically in highly
nonlinear or high-dimensional problems.
General Recommendations
Start simple: Always begin with classical models
(linear/logistic regression) as baselines. Their interpretability and
efficiency make them ideal for initial exploration.
Use MLPs judiciously: Reserve MLPs for cases
where linear models underperform due to clear nonlinearity or
interaction effects. Prefer single-hidden-layer architectures unless
deeper networks demonstrably improve performance.
Balance complexity and performance: Avoid
unnecessary model complexity—favor the simplest model that achieves
satisfactory accuracy.
Hybrid approach: Combine linear models with
feature engineering or kernel methods before resorting to MLPs,
particularly in resource-constrained settings.
Ultimately, model choice should be guided by problem constraints,
data structure, and the trade-offs between accuracy, interpretability,
and computational cost. A systematic, iterative approach—from linear
models to shallow then deep networks—ensures both efficiency and
performance.
LS0tDQp0aXRsZTogJ011bHRpbGF5ZXIgTmV1cmFsIE5ldHdvcmtzIGZvciBDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbicNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQgDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmxpYnJhcnkoZTEwNzEpDQp9DQppZiAoIXJlcXVpcmUoIm1tZWxuIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1tZWxuIikNCmxpYnJhcnkobW1lbG4pDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQpsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQpsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmxpYnJhcnkoY2FyZXQpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmxpYnJhcnkocnBhcnQpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQp9DQppZiAoIXJlcXVpcmUoImlwcmVkIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImlwcmVkIikNCmxpYnJhcnkoaXByZWQpDQp9DQppZiAoIXJlcXVpcmUoIm1sYmVuY2giKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibWxiZW5jaCIpDQpsaWJyYXJ5KG1sYmVuY2gpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpDQpsaWJyYXJ5KHBST0MpDQp9DQppZiAoIXJlcXVpcmUoIm5ldXJhbG5ldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQ0KbGlicmFyeShuZXVyYWxuZXQpDQp9DQppZiAoIXJlcXVpcmUoIm5ldXJhbG5ldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQ0KbGlicmFyeShuZXVyYWxuZXQpDQp9DQppZiAoIXJlcXVpcmUoIk5ldXJhbE5ldFRvb2xzIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk5ldXJhbE5ldFRvb2xzIikNCmxpYnJhcnkoTmV1cmFsTmV0VG9vbHMpDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJw0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQoqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiosIG9mdGVuIHJlZmVycmVkIHRvIGFzICoqZGVlcCBsZWFybmluZyBtb2RlbHMqKiBvciAgKiptdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyoqLCBhcmUgYSBwb3dlcmZ1bCBjbGFzcyBvZiBhbGdvcml0aG1zIHdpdGggd2lkZSBhcHBsaWNhdGlvbnMuIFRoZXNlIG5ldHdvcmtzIGNvbnNpc3Qgb2YgaW50ZXJjb25uZWN0ZWQgbGF5ZXJzIG9mIGFydGlmaWNpYWwgIm5ldXJvbnMsIiBlYWNoIHByb2Nlc3NpbmcgaW5mb3JtYXRpb24gYW5kIHBhc3NpbmcgaXQgZm9yd2FyZCB0byBzdWJzZXF1ZW50IGxheWVycy4gVGhlIGZpcnN0IGxheWVyIHJlY2VpdmVzIHJhdyBpbnB1dCBkYXRhLCB3aGlsZSBoaWRkZW4gbGF5ZXJzIHRyYW5zZm9ybSB0aGlzIGRhdGEgdGhyb3VnaCB3ZWlnaHRlZCBjb25uZWN0aW9ucyBhbmQgbm9ubGluZWFyIGZ1bmN0aW9ucywgYWxsb3dpbmcgdGhlIG5ldHdvcmsgdG8gbGVhcm4gY29tcGxleCBwYXR0ZXJucy4gVGhlIGZpbmFsIGxheWVyIHByb2R1Y2VzIHByZWRpY3Rpb25zIG9yIGNsYXNzaWZpY2F0aW9ucy4NCg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL01MUDAucG5nIikNCmBgYA0KDQoNCkluIHN0YXRpc3RpY2FsIG1vZGVsaW5nLCBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyBjYW4gaGFuZGxlIGludHJpY2F0ZSByZWxhdGlvbnNoaXBzIGluIGRhdGEuIFVubGlrZSBsaW5lYXIgcmVncmVzc2lvbiBvciBzaW1wbGUgZGVjaXNpb24gdHJlZXMsICBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrIG1vZGVscyBjYW4gbW9kZWwgaGlnaGx5IG5vbmxpbmVhciBpbnRlcmFjdGlvbnMgYW5kIGFkYXB0IHRvIGxhcmdlLXNjYWxlLCBoaWdoLWRpbWVuc2lvbmFsIGRhdGFzZXRzIHdoZXJlIGNsYXNzaWMgc3RhdGlzdGljYWwgbW9kZWxzIG9mdGVuIHN0cnVnZ2xlLiBUaGVpciBmbGV4aWJpbGl0eSBjb21lcyBmcm9tIHRoZWlyIGFiaWxpdHkgdG8gaXRlcmF0aXZlbHkgYWRqdXN0IHdlaWdodHMgZHVyaW5nIHRyYWluaW5nLCByZWZpbmluZyB0aGVpciBwcmVkaWN0aW9ucyBhcyB0aGV5IGVuY291bnRlciBtb3JlIGRhdGEuDQoNCkRlc3BpdGUgdGhlaXIgc3RyZW5ndGhzLCBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyByZXF1aXJlIGNhcmVmdWwgdHVuaW5nIGFuZCBsYXJnZSBkYXRhc2V0cyB0byBwZXJmb3JtIHdlbGwuIA0KDQoqIE92ZXJmaXR0aW5n4oCUd2hlcmUgYSBtb2RlbCBtZW1vcml6ZXMgdHJhaW5pbmcgZGF0YSBidXQgZmFpbHMgb24gbmV3IGlucHV0c+KAlGlzIGEgY29tbW9uIGNoYWxsZW5nZSwgbWl0aWdhdGVkIGJ5IHRlY2huaXF1ZXMgbGlrZSBkcm9wb3V0IGFuZCByZWd1bGFyaXphdGlvbi4gDQoNCiogVGhlaXIgKipibGFjayBib3gqKiBuYXR1cmUgY2FuIGFsc28gbWFrZSBpbnRlcnByZXRpbmcgcmVzdWx0cyBkaWZmaWN1bHQsIHBvc2luZyBjaGFsbGVuZ2VzIGluIGZpZWxkcyB3aGVyZSBleHBsYWluYWJpbGl0eSBpcyBjcnVjaWFsLCBzdWNoIGFzIGhlYWx0aGNhcmUgb3IgZmluYW5jZS4gDQoNCioqTUxQKipzIGFyZSBhIGZvdW5kYXRpb25hbCBhcmNoaXRlY3R1cmUgaW4gbW9kZXJuIG1hY2hpbmUgbGVhcm5pbmcgYW5kIGFydGlmaWNpYWwgaW50ZWxsaWdlbmNlLiBUaGVpciBhYmlsaXR5IHRvIGFwcHJveGltYXRlIGNvbXBsZXgsIG5vbmxpbmVhciBmdW5jdGlvbnMgbWFrZXMgdGhlbSBoaWdobHkgdmVyc2F0aWxlIGZvciBhIHdpZGUgcmFuZ2Ugb2YgcmVhbC13b3JsZCBwcm9ibGVtcy4gDQoNClRoZXJlIGFyZSB2YXJpb3VzIGFkYXB0YXRpb25zIGFuZCBleHRlbnNpb25zIG9mIE1MUHMsIGVhY2ggZGV2ZWxvcGVkIGZvciBzcGVjaWZpYyBwdXJwb3NlcyBhY3Jvc3MgZGlmZmVyZW50IGZpZWxkcy4gQSBzb2xpZCB1bmRlcnN0YW5kaW5nIG9mIE1MUHMgaXMgZXNzZW50aWFsIGZvciBncmFzcGluZyB0aGUgdW5kZXJseWluZyBwcmluY2lwbGVzIG9mIG1hbnkgc3BlY2lhbGl6ZWQgbmV1cmFsIG5ldHdvcmsgYWxnb3JpdGhtcy4NCg0KXA0KDQojIEFyY2hpdGVjdHVyZSBvZiBNTFANCg0KQSBNdWx0aWxheWVyIFBlcmNlcHRyb24gKE1MUCkgaXMgZ2VuZXJhbGl6ZWQgZnJvbSB0aGUgcGVyY2VwdHJvbiBieSBhZGRpbmcgb25lIG9yIG1vcmUgaGlkZGVuIGxheWVycyBiZXR3ZWVuIHRoZSBpbnB1dCBhbmQgb3V0cHV0IGxheWVycy4gVGhhdCBpcywgYW4gTUxQIGhhcyB0aHJlZSBtYWpvciBjb21wb25lbnRzOg0KDQotIEFuICoqaW5wdXQgbGF5ZXIqKg0KLSBPbmUgb3IgbW9yZSAqKmhpZGRlbiBsYXllcnMqKiANCi0gQW4gKipvdXRwdXQgbGF5ZXIqKg0KDQpFYWNoIGxheWVyIGNvbnNpc3RzIG9mIG11bHRpcGxlICoqbmV1cm9ucyAobm9kZXMpKiogY29ubmVjdGVkIHZpYSB3ZWlnaHRzLCB3aXRoIG5vbmxpbmVhciBhY3RpdmF0aW9uIGZ1bmN0aW9ucyB0aGF0IGNhcHR1cmUgY29tcGxleCByZWxhdGlvbnNoaXBzIGJldHdlZW4gZmVhdHVyZXMgYW5kIHRoZSB0YXJnZXQsIHRoZXJlYnkgaW1wcm92aW5nIHRoZSBhbGdvcml0aG0ncyBwZXJmb3JtYW5jZS4NCg0KSWYgYW4gTUxQIGhhcyBvbmx5IG9uZSBoaWRkZW4gbGF5ZXIsIHRoaXMgTUxQIGlzIHVzdWFsbHkgY2FsbGVkICoqc2hhbGxvdyBuZXVyYWwgbmV0d29yayoqLiAqKkRlZXAgbmV1cmFsIG5ldHdvcmtzKiogaGF2ZSB0d28gb3IgbW9yZSBoaWRkZW4gbGF5ZXJzLg0KDQpJbiB0aGUgc3Vic2VxdWVudCBzZWN0aW9ucywgd2Ugd2lsbCBicmllZmx5IGludHJvZHVjZSB0aGUgdGhlb3J5IG9mIG5ldXJhbCBuZXR3b3JrcyB3aXRoIGFuIGVtcGhhc2lzIG9uIHNoYWxsb3cgbmV0d29ya3MuIFNvbWUgY2FzZSBzdHVkaWVzIHdpbGwgYWxzbyBiZSBpbmNsdWRlZCB0byBkZW1vbnN0cmF0ZSB0aGUgYXBwbGljYXRpb24gb2YgbmV1cmFsIG5ldHdvcmsgbW9kZWxzIGluIHJlZ3Jlc3Npb24gYW5kIGNsYXNzaWZpY2F0aW9uLg0KDQoNCg0KIyMgTWF0aGVtYXRpY3Mgb2YgTUxQDQoNCkZvciBpbGx1c3RyYXRpb24sIHdlIHVzZSB0aGUgZm9sbG93aW5nIG9uZS1oaWRkZW4tbGF5ZXIgTUxQIHJlZ3Jlc3Npb24gdG8gZXhwbGFpbiB0aGUgbWF0aGVtYXRpY2FsIGZvdW5kYXRpb25zIG9mIHRoZSBhbGdvcml0aG0uIFRoZSBvYmplY3RpdmUgaXMgdG8gcHJlZGljdCB0aGUgdGFyZ2V0IHZhcmlhYmxlIA0KJHkkIHVzaW5nIGlucHV0IGZlYXR1cmVzICRce3hfMSwgeF8yLCBcY2RvdHMsIHhfbiBcfSQuIElmIGFjdGl2YXRpb24gZnVuY3Rpb25zICRccGhpXzEoXGNkb3QpJCBhbmQgJFxwaGkoXGNkb3QpJCBhcmUgY29ycmVjdGx5IHNwZWNpZmllZCwgdGhlbiB0aGUgdGFyZ2V0IGNhbiBiZSBleHByZXNzZWQgYXMgYSBjb21wb3NpdGlvbiBvZiB3ZWlnaHRzIHRocm91Z2ggZnVuY3Rpb24gY29tcG9zaXRpb24uDQoNCg0KYGBge3IgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9NTFAwMS5wbmciKQ0KYGBgDQoNCkluIGVzc2VuY2UsIE1MUCBpcyBhIHByb2JsZW0gb2YgYXBwcm94aW1hdGluZyBhIG11bHRpLXZhcmlhYmxlIGZ1bmN0aW9uLiBBc3N1bWUgdGhhdCB0aGVyZSBhcmUgJG4kIGZlYXR1cmVzIGluIHRoZSBzY2VuYXJpbyBvZiB0aGUgYWJvdmUgb25lLWhpZGRlbi1sYXllciBNTFAsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdGFyZ2V0IHZhcmlhYmxlICR5JCBpcyBjaGFyYWN0ZXJpemVkIGJ5ICR5ID0gZih4XzEsIHhfMiwgXGNkb3RzLCB4X246IFxtYXRoYmZ7d30pJCwgd2hlcmUgJGYoXGNkb3QpJCBpcyB1bmtub3duIGFuZCAkXG1hdGhiZnt3fSQgaXMgdGhlIHZlY3RvciBvZiB1bmtub3duIHBhcmFtZXRlcnMuIFRoYXQgaXMsIGluIHRoZSBhYm92ZSBNTFAsIHRoZSB0YXJnZXQgY2FuIGJlIGV4cHJlc3NlZCBhcw0KDQokJA0KeSA9IGYoeF8xLCB4XzIsIFxjZG90cywgeF9uOiBcbWF0aGJme3d9KSA9IFxwaGlfMltccGhpXzEoeF8xLCB4XzIsIFxjZG90cywgeF9uOiBcbWF0aGJme3d9KV0uDQokJA0KDQo8Zm9udCBjb2xvciA9ICJyZWQiPioqXGNvbG9ye3JlZH1Ob3RlIHRoYXQgJGYoXGNkb3QpJCBpcyBhbiBhcmJpdHJhcnkgZnVuY3Rpb24gJGY6IFxtYXRoYmJ7Un1ebiBccmlnaHRhcnJvdyBcbWF0aGJie1J9JC4qKjwvZm9udD4gRm9yIGEgZ2l2ZW4gZGF0YSBzZXQgJFx7KHhfezFpfSwgeF97Mml9LCBcY2RvdHMsIHhfe25pfSlcfV97aT0xfV5uJCBhbmQgZXN0aW1hdGVkICRcaGF0e1xtYXRoYmZ7d319JCwgZGVwZW5kaW5nIG9uIGFwcGxpY2F0aW9ucywgdGhlIGxvc3MgZnVuY3Rpb24gaXMgZGVmaW5lZCByZXNwZWN0aXZlbHkgYnkNCg0KKiAqKkNvbnRpbnVvdXMgUmVncmVzc2lvbiAoTVNFKSoqDQoNCiQkDQogXG1hdGhjYWx7TH0gPSBcZnJhY3sxfXtOfSBcc3VtX3tjPTF9XkMgKHlfaSAtIFxoYXR7eX1faSleMiANCiQkDQoNCiogKipEaXNjcmV0ZSBDbGFzc2lmaWNhdGlvbiAoQ3Jvc3MtZW50cm9weSkqKg0KDQokJA0KIFxtYXRoY2Fse0x9ID0gLSBcc3VtX3tjPTF9XkMgIHlfYyBcbG9nKFxoYXR7eX1fYykNCiQkDQpmb3IgJEMkIGNsYXNzZXMuIFVzaW5nIHRoZSBncmFkaWVudCBkZWNlbnQgbWV0aG9kLCB3ZSBjYW4gZXhwcmVzcyB0aGUgd2VpZ2h0IHVwZGF0aW5nIGZvcm11bGEgaW4gdGhlIGZvbGxvd2luZw0KDQokJA0KXG1hdGhiZntXfV57KGorMSl9IFxsZWZ0YXJyb3cgXG1hdGhiZntXfV57KGopfSAtIFxldGEgXGZyYWN7XHBhcnRpYWwgXG1hdGhjYWx7TH19e1xwYXJ0aWFsIFxtYXRoYmZ7V319XEJpZ3xfe1xtYXRoYmZ7V309XG1hdGhiZntXfV57KGopfX0NCiQkDQp3aGVyZSAkXGV0YSQgaXMgdGhlIGh5cGVycGFyYW1ldGVyIG9mICoqbGVhcm5pbmcgcmF0ZSoqLiAgDQoNCg0KIyMgVW5pdmVyc2FsIEFwcHJveGltYXRpb24gVGhlb3JlbQ0KDQpUaGUgKipVbml2ZXJzYWwgQXBwcm94aW1hdGlvbiBUaGVvcmVtKiogaXMgYSBmdW5kYW1lbnRhbCByZXN1bHQgaW4gdGhlIHRoZW9yeSBvZiBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3Jrcywgc3RhdGluZyB0aGF0IGEgZmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsgd2l0aCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgY29udGFpbmluZyBhIGZpbml0ZSBudW1iZXIgb2YgbmV1cm9ucyBjYW4gYXBwcm94aW1hdGUgY29udGludW91cyBmdW5jdGlvbnMgb24gY29tcGFjdCBzdWJzZXRzIG9mICRcbWF0aGJie1J9Xm4kIHVuZGVyIG1pbGQgY29uZGl0aW9ucyBvbiB0aGUgYWN0aXZhdGlvbiBmdW5jdGlvbi4gVGhlIGZvcm1hbCBzdGF0ZW1lbnQgaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZzogICAgDQoNCioqQ3liZW5rbydzIFRoZW9yZW0gKDE5ODkpKio6IExldCAkXHBoaShcY2RvdCkkIGJlIGEgY29udGludW91cywgbm9uLWNvbnN0YW50IGFjdGl2YXRpb24gZnVuY3Rpb24gKGUuZy4sIHNpZ21vaWQpLiBHaXZlbiBhbnkgY29udGludW91cyBmdW5jdGlvbiAkZjogWzAsMV1ebiBccmlnaHRhcnJvdyBcbWF0aGJie1J9JCBhbmQgJFxlcHNpbG9uID4gMCQsIHRoZXJlIGV4aXN0cyBhICoqc2luZ2xlLWhpZGRlbi1sYXllciBNTFAqKiAobXVsdGlsYXllciBwZXJjZXB0cm9uKSB3aXRoIGZpbml0ZWx5IG1hbnkgaGlkZGVuIG5vZGVzIChuZXVyb25zKSwgZGVub3RlZCBieSAkTihcbWF0aGJme3h9KSQsIHN1Y2ggdGhhdCANCg0KJCQNClxzdXBfe1xtYXRoYmZ7eH1caW4gWzAsMV1ebn0gXGJpZ3xmKFxtYXRoYmZ7eH0pIC0gTihcbWF0aGJme3h9KSBcYmlnfCA8IFxlcHNpbG9uLg0KJCQNCg0KVGhlIGFib3ZlIGFuYWx5c2lzIGFzc3VtZXMgdGhlIHN1cHBvcnQgb2YgdGhlIG11bHRpdmFyaWFibGUgZnVuY3Rpb24gaXMgJFswLDFdXm4kLiBUaGlzIGNhbiBiZSBlYXNpbHkgZ2VuZXJhbGl6ZWQgdG8gYW55IGZ1bmN0aW9uIHdpdGggZ2VuZXJhbCBmaW5pdGUgc3VwcG9ydCAkW2EtMSwgYl8xXVx0aW1lcyBbYV8yLCBiXzJdIFx0aW1lcyBcY2RvdHNcdGltZXNbYV9rLCBiX2tdJCB2aWEgYSBsaW5lYXIgdHJhbnNmb3JtYXRpb24uIEZvciBleGFtcGxlLCBhc3N1bWUgJGZfayh4X2spJCBoYXMgc3VwcG9ydCAkW2FfaywgYl9rXSQsIHdlIGNhbiB1c2UgbGluZWFyIHRyYW5zZm9ybWF0aW9uICR5X2sgPSAoeF9rIC0gYV9rKS8oYl9rIC0gYV9rKSQgd2hpY2ggaW1wbGllcyB0aGF0ICR5X2sgXGluIFswLDFdJC4gDQoNClRoaXMgZXhwbGFpbnMgd2h5IGEgKipvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24qKiBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24gZGVmaW5lZCBvbiBhIGZpbml0ZSBzdXBwb3J0IHVzaW5nIGZpbml0ZWx5IG1hbnkgbm9kZXMgaW4gdGhlIGhpZGRlbiBsYXllci4gRm9yIGZ1bmN0aW9ucyB3aXRoIGluZmluaXRlIHN1cHBvcnQsIHdlIGNhbiBmaXJzdCBhcHByb3hpbWF0ZSB0aGVtIG9uIGEgZmluaXRlIGRvbWFpbiBhbmQgdGhlbiBhcHBseSB0aGUgKip1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtKiogdG8gYWNoaWV2ZSB0aGUgZGVzaXJlZCByZXN1bHQuDQoNCg0KUmVjYWxsIHRoYXQsIHRoZSBvYmplY3RpdmUgb2YgcmVncmVzc2lvbiBhbmFseXNpcyBpcyB0byBlc3RpbWF0ZSB0aGUgKip1bmtub3duKiogcmVncmVzc2lvbiBmdW5jdGlvbiBiYXNlZCBvbiBhIHNhbXBsZSB0YWtlbiBmcm9tIHRoZSB1bmRlcmx5aW5nIHBvcHVsYXRpb24gYW5kIHRoZW4gdXNlIHRoZSAqKmVzdGltYXRlZCoqIHJlZ3Jlc3Npb24gZnVuY3Rpb24gdG8gDQoNCiogYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmZWF0dXJlIHZhcmlhYmxlcyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZTsNCiogcHJlZGljdCB0aGUgdmFsdWUgb2YgdGhlIHRhcmdldCB2YXJpYWJsZSBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSBpbnB1dCBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KVGhlICoqdW5pdmVyc2FsIGFwcHJveGltYXRpb24gdGhlb3JlbSoqIGZvciBzaGFsbG93IG5ldHdvcmtzIGd1YXJhbnRlZXMgdGhlaXIgY2FwYWJpbGl0eSBhcyBhICoqZnVuY3Rpb24gYXBwcm94aW1hdG9yKiosIG1ha2luZyBpdCBhIG5hdHVyYWwgdGhlb3JldGljYWwgZm91bmRhdGlvbiBmb3IgbmV1cmFsIG5ldHdvcmsgcmVncmVzc2lvbiBtZXRob2RzLg0KDQoNCg0KIyMgU29tZSBHcmFwaGljYWwgRGVtb25zdHJhdGlvbnMNCg0KVGhpcyBzdWJzZWN0aW9uIHVzZXMgYSBzaW11bGF0aW9uIGFwcHJvYWNoIHRvIGRlbW9uc3RyYXRlIHdoeSBNTFAgaXMgYWxzbyBhIG5vbi1jbGFzc2ljYWwgZmFtaWx5IG9mIGFsZ29yaXRobWljIHJlZ3Jlc3Npb24gbW9kZWxzLiBXZSB0YWtlIGEgc2V0IG9mIHBvaW50cyBvbiB0aGUgc3VyZmFjZSBvZiBhIGh5cG90aGVzaXplZCBlbGxpcHRpYyBwYXJhYm9sb2lkIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdGhlbiB1c2UgTUxQIHRvIGVzdGltYXRlIHRoZSBoeXBvdGhldGljYWwgZWxsaXB0aWMgcGFyYWJvbG9pZCBtb2RlbC4gVGhlIGVsbGlwdGljIHBhcmFib2xvaWQgdXNlZCB0byBnZW5lcmF0ZSBkYXRhIGlzIGdpdmVuIGJ5DQoNCiQkDQp6ID0geF8xXjIgKyB4XzJeMg0KJCQNClRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBoeXBvdGhldGljYWwgcmVncmVzc2lvbiBzdXJmYWNlLCBhbG9uZyB3aXRoIHRoZSBkYXRhIHBvaW50cyB1c2VkIHRvIGVzdGltYXRlIGl0Lg0KDQoNCmBgYHtyfQ0KI2xpYnJhcnkocGxvdGx5KQ0KDQojIERlZmluZSB0aGUgZWxsaXB0aWMgcGFyYWJvbG9pZCBmdW5jdGlvbjogeiA9ICh4XjIgLyBhXjIpICsgKHleMiAvIGJeMikNCiMgQ3JlYXRlIGEgZ3JpZCBvZiB4IGFuZCB5IHZhbHVlcw0KeCA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjAwKQ0KeSA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjAwKQ0KeiA8LSBvdXRlcih4LCB5LCBmdW5jdGlvbih4LCB5KSAoeF4yICArIHleMiApKQ0KDQojIyBzYW1wbGUgcG9pbnRzIG9uIHRoZSBzdXJmYWNlIHRvIGRlZmluZSBhIGRhdGEgc2V0DQp4LnNpbSA8LSBzYW1wbGUoeCkNCnkuc2ltIDwtIHNhbXBsZSh5KQ0Kei5zYW1wbGUgPC0gKHguc2ltXjIgKyB5LnNpbV4yKSArIHJub3JtKDIwMCwgMCwgMC41KQ0KDQojIFBsb3QgbWFyZ2lucw0KbSA8LSBsaXN0KCBsID0gNTAsIHIgPSA1MCwgYiA9IDEwMCwgdCA9IDEwMCwgcGFkID0gNCkNCg0KIyBDcmVhdGUgdGhlIDNEIHN1cmZhY2UgcGxvdA0KcGxvdF9seSh4ID0geCwgeSA9IHksIHogPSB6LCB0eXBlID0gInN1cmZhY2UiKSAlPiUgICMgdGhlIHN1cmZhY2UNCiAgYWRkX21hcmtlcnMoeCA9IHguc2ltLCAgICMgc2FtcGxlIHBvaW50cyBuZWFyIHRoZSBzdXJmYWNlDQogICAgICAgICAgICAgIHkgPSB5LnNpbSwgDQogICAgICAgICAgICAgIHogPSB6LnNhbXBsZSwgICANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KG1hcmdpbiA9IG0sIA0KICAgICAgICAgIHRpdGxlID0gIkVsbGlwdGljIFBhcmFib2xvaWQ6IEh5cG90aGVzaXplZCBNb2RlbCBmb3IgRGF0YSBHZW5lcmF0aW9uIiwNCiAgICAgICAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlgiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKQ0KICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgKQ0KYGBgDQoNCg0KSW4gcHJhY3RpY2UsIHdlIG9ubHkgb2JzZXJ2ZSBkYXRhIHBvaW50cyBmcm9tIGFuIHVua25vd24gc3VyZmFjZS4gV2UgdGhlcmVmb3JlIHJlbW92ZSB0aGUgaHlwb3RoZXRpY2FsIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdmlzdWFsaXplIGp1c3QgdGhlIGRhdGEgcG9pbnRzIHRvIHJldmVhbCB0aGUgZGF0YSBjbG91ZCdzIHN0cnVjdHVyZS4NCg0KDQpgYGB7cn0NCnBsb3RfbHkoKSAlPiUNCiAgYWRkX21hcmtlcnMoeCA9IHguc2ltLCB5ID0geS5zaW0sIHogPSB6LnNhbXBsZSwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCggbWFyZ2luID0gbSwNCiAgICAgICAgICAgdGl0bGUgPSAiU2FtcGxlIHBvaW50cyBmcm9tIHRoZSBlbGxpcHRpYyBwYXJhYm9sb2lkIiwNCiAgICAgICAgICAgc2NlbmUgPSBsaXN0KCB4YXhpcyA9IGxpc3QodGl0bGUgPSAiWCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJZIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKQ0KICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgKQ0KYGBgDQoNClRoZXJlIGlzIGEgbm9ubGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0YXJnZXQgdmFyaWFibGUgKCR6JCkgYW5kIGZlYXR1cmUgdmFyaWFibGVzICgkeF8xJCBhbmQgJHhfMiQpLiBJbiB0aGUgZm9sbG93aW5nIGZpZ3VyZXMsIHdlIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRocmVlIG1vZGVscyBmaXR0ZWQgdG8gdGhlIHNhbWUgZGF0YXNldDogYSBzaW5nbGUgcGVyY2VwdHJvbiwgYSBvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24sIGFuZCBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbi4gV2UgdGhlbiBwbG90IHRoZWlyIGNvcnJlc3BvbmRpbmcgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZXMgdG8gdmlzdWFsaXplIHRoZSBnb29kbmVzcyBvZiBmaXQuDQoNCg0KYGBge3J9DQojbGlicmFyeShuZXVyYWxuZXQpDQojbGlicmFyeShnZ3Bsb3QyKQ0KI2xpYnJhcnkocGxvdGx5KSAgIyBGb3IgM0QgdmlzdWFsaXphdGlvbg0KDQojIEdlbmVyYXRlIHRyYWluaW5nIGRhdGENCiNzZXQuc2VlZCgxMjMpDQojeCA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjApDQojeSA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjApDQojel90cmFpbiA8LSB4X3RyYWluXjIgKyB5X3RyYWluXjIgIyArIHJub3JtKDUwMCwgc2QgPSAwLjEpICAjIEFkZGVkIG5vaXNlDQoNCiMjIFRoZSB3b3JraW5nIGRhdGEgc2V0OiB3ZSBjYWxsIGl0IHRoZSB0cmFpbmluZyBkYXRhIHNldA0KdHJhaW4uZGF0YSA8LSBkYXRhLmZyYW1lKHggPSB4LnNpbSwgeSA9IHkuc2ltLCB6ID0gei5zYW1wbGUpDQoNCiMjIyBubyBoaWRkZW4gbGF5ZXINCmhpZGRlbjAuMCA8LSBuZXVyYWxuZXQoeiB+IHggKyB5LCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgIHN0ZXBtYXggPSAxZTYpICAjIEluY3JlYXNlIGl0ZXJhdGlvbnMNCg0KDQoNCiMjIyBzaW5nbGUgaGlkZGVuIGxheWVyDQpoaWRkZW4xLjggPC0gbmV1cmFsbmV0KHogfiB4ICsgeSwgDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gYyg4KSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbmVhci5vdXRwdXQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBzdGVwbWF4ID0gMWU2KSAgIyBJbmNyZWFzZSBpdGVyYXRpb25zDQoNCg0KDQojIFRyYWluIG5ldXJhbCBuZXR3b3JrIHdpdGggMiBoaWRkZW4gbGF5ZXJzICg4IGFuZCA0IG5ldXJvbnMpDQpoaWRkZW4yLjguNCA8LSBuZXVyYWxuZXQoeiB+IHggKyB5LCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDgsNCksIA0KICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXIub3V0cHV0ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RlcG1heCA9IDFlNikgICMgSW5jcmVhc2UgaXRlcmF0aW9ucw0KDQojIENyZWF0ZSB0ZXN0IGdyaWQgdG8gZHJhdyB0aGUgUHJlZGljdGVkIHN1cmZhY2UgYmFzZWQgb24gZGlmZmVyZW50IE5OIG1vZGVscw0KeC50ZXN0IDwtIHNlcSgtMiwgMiwgbGVuZ3RoLm91dCA9IDIwMCkNCnkudGVzdCA8LSBzZXEoLTIsIDIsIGxlbmd0aC5vdXQgPSAyMDApDQp0ZXN0LmdyaWQgPC0gZXhwYW5kLmdyaWQoeCA9IHgudGVzdCwgeSA9IHkudGVzdCkNCnoudGVzdDAuMCA8LSBtYXRyaXgocHJlZGljdChoaWRkZW4wLjAsIHRlc3QuZ3JpZCksbmNvbD0yMDApDQp6LnRlc3QxLjggPC0gbWF0cml4KHByZWRpY3QoaGlkZGVuMS44LCB0ZXN0LmdyaWQpLG5jb2w9MjAwKQ0Kei50ZXN0Mi44LjQgPC0gbWF0cml4KHByZWRpY3QoaGlkZGVuMi44LjQsIHRlc3QuZ3JpZCksbmNvbD0yMDApDQoNCmBgYA0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgcHJlZGljdGVkIHN1cmZhY2UgZnJvbSB0aGUgcGVyY2VwdHJvbiBtb2RlbCwgd2hpY2ggcHJvZHVjZXMgYSBsaW5lYXIgaHlwZXJwbGFuZSwgYWxvbmcgd2l0aCB0aGUgZGF0YSBwb2ludHMgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcmVncmVzc2lvbiBzdXJmYWNlLiBUaGUgcGVyY2VwdHJvbiBmaXRzIHRoZSBkYXRhIHBvb3JseS4NCg0KDQpgYGB7cn0NCiMjIHNldCB1cCBwbG90IG1hcmdpbg0KbSA8LSBsaXN0KGwgPSA1MCxyID0gNTAsIGIgPSAxMDAsIHQgPSAxMDAsIHBhZCA9IDQpDQoNCiMgM0QgVmlzdWFsaXphdGlvbiB3aXRoIHBsb3RseQ0KcGxvdF9seSh4ID0gfngudGVzdCwgeSA9IH55LnRlc3QsIHogPSB+ei50ZXN0MC4wLCB0eXBlID0gInN1cmZhY2UiKSAlPiUNCiAgYWRkX21hcmtlcnMoZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56LCANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ1BlcmNlcHRyb24gTW9kZWwgKHdpdGggbm8gaGlkZGVuIGxheWVyKScsDQogICAgICAgICBhdXRvc2l6ZSA9IFQsIA0KICAgICAgICAgI3dpZHRoID0gNTAwLCANCiAgICAgICAgICNoZWlnaHQgPSA1MDAsDQogICAgICAgICBtYXJnaW4gPSBtLCANCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiWCIpLA0KICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlkiKSwNCiAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJaIikpKQ0KYGBgDQoNCg0KTmV4dCwgd2UgZml0IGEgKipvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24gd2l0aCA4IG5vZGVzKiogdG8gdGhlIHNhbWUgZGF0YXNldC4gVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgYm90aCB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdGhlIGRhdGEgcG9pbnRzLiBXZSBvYnNlcnZlIHRoYXQgdGhlIHByZWRpY3RlZCBzdXJmYWNlIGNsb3NlbHkgYXBwcm94aW1hdGVzIHRoZSAqKmh5cG90aGVzaXplZCByZWdyZXNzaW9uIHN1cmZhY2UqKi4gTm90ZSB0aGF0IHRoZSBudW1iZXIgb2Ygbm9kZXMgaW4gdGhlIGhpZGRlbiBsYXllciBpcyBhIHR1bmFibGUgaHlwZXJwYXJhbWV0ZXIgdGhhdCBjYW4gYmUgb3B0aW1pemVkIHRvIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UuDQoNCg0KYGBge3J9DQojIDNEIFZpc3VhbGl6YXRpb24gd2l0aCBwbG90bHkNCnBsb3RfbHkoeCA9IH54LnRlc3QsIHkgPSB+eS50ZXN0LCB6ID0gfnoudGVzdDEuOCwgdHlwZSA9ICJzdXJmYWNlIikgJT4lDQogIGFkZF9tYXJrZXJzKGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgeCA9IH54LCB5ID0gfnksIHogPSB+eiwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdFc3RpbWF0ZWQgT25lLWhpZGRlbi1sYXllciBQZXJjZXB0cm9uICh3aXRoIDggbm9kZXMpJywNCiAgICAgICAgIGF1dG9zaXplID0gVCwgDQogICAgICAgICAjd2lkdGggPSA1MDAsIA0KICAgICAgICAgI2hlaWdodCA9IDUwMCwNCiAgICAgICAgIG1hcmdpbiA9IG0sIA0KICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKSkpDQpgYGANCg0KRmluYWxseSwgd2UgZXhhbWluZSBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBhcmNoaXRlY3R1cmUsIHdpdGggOCBub2RlcyBpbiB0aGUgZmlyc3QgaGlkZGVuIGxheWVyIGFuZCA0IG5vZGVzIGluIHRoZSBzZWNvbmQgaGlkZGVuIGxheWVyLCB0cmFpbmVkIG9uIHRoZSBzYW1lIGRhdGEgc2V0Lg0KDQoNCmBgYHtyfQ0KIyAzRCBWaXN1YWxpemF0aW9uIHdpdGggcGxvdGx5DQpwbG90X2x5KHggPSB+eC50ZXN0LCB5ID0gfnkudGVzdCwgeiA9IH56LnRlc3QyLjguNCwgdHlwZSA9ICJzdXJmYWNlIikgJT4lDQogIGFkZF9tYXJrZXJzKGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgeCA9IH54LCB5ID0gfnksIHogPSB+eiwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdFc3RpbWF0ZWQgVHdvLWhpZGRlbi1sYXllciBQZXJjZXB0cm9uICh3aXRoIDggYW5kIDQgbm9kZXMpJywNCiAgICAgICAgIGF1dG9zaXplID0gVCwgDQogICAgICAgICAjd2lkdGggPSA1MDAsIA0KICAgICAgICAgI2hlaWdodCA9IDUwMCwNCiAgICAgICAgIG1hcmdpbiA9IG0sIA0KICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKSkpDQpgYGANCg0KVGhlIGZpZ3VyZSBhYm92ZSBzaG93cyBib3RoIHRoZSBlc3RpbWF0ZWQgcHJlZGljdGlvbiBzdXJmYWNlIGFuZCB0aGUgb3JpZ2luYWwgZGF0YSBwb2ludHMuIFdlIG9ic2VydmUgYSBzaWduaWZpY2FudCBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSAqKmVzdGltYXRlZCByZWdyZXNzaW9uIHN1cmZhY2UqKiBhbmQgdGhlICoqaHlwb3RoZXNpemVkICh0cnVlKSByZWdyZXNzaW9uIHN1cmZhY2UqKi4gVXBvbiBjbG9zZXIgZXhhbWluYXRpb24sIHdlIGZpbmQgdGhhdCB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZSB5aWVsZHMgc21hbGwgcmVzaWR1YWxzLCBhcyB0aGUgZGF0YSBwb2ludHMgbGllIGNsb3NlIHRvIHRoZSBmaXR0ZWQgc3VyZmFjZS4gVGhpcyBzdWdnZXN0cyBwb3RlbnRpYWwgKipvdmVyZml0dGluZyoqIGluIHRoZSB0d28taGlkZGVuLWxheWVyIHBlcmNlcHRyb24gbW9kZWwuDQoNCg0KIyBNTFAgTW9kZWxpbmcgUHJvY2VzcyANCg0KTUxQcyBjYW4gbGVhcm4gY29tcGxleCwgbm9uLWxpbmVhciByZWxhdGlvbnNoaXBzIGR1ZSB0byB0aGVpciBtdWx0aXBsZSBsYXllcnMgb2YgaW50ZXJjb25uZWN0ZWQgbmV1cm9ucy4gSG93ZXZlciwgZGVzaWduaW5nIGFuZCB0cmFpbmluZyBhbiBlZmZlY3RpdmUgTUxQIHJlcXVpcmVzIGNhcmVmdWwgY29uc2lkZXJhdGlvbiBvZiBkYXRhIHByZXByb2Nlc3NpbmcsIGFyY2hpdGVjdHVyZSBzZWxlY3Rpb24sIG9wdGltaXphdGlvbiB0ZWNobmlxdWVzLCBhbmQgb3RoZXIgc3RyYXRlZ2llcy4NCg0KIyMgRGF0YSBQcmVwcm9jZXNzaW5nDQoNCkJlZm9yZSB0cmFpbmluZyBhbiBNTFAsIHRoZSBpbnB1dCBkYXRhIG11c3QgYmUgcHJvcGVybHkgcHJlcGFyZWQgdG8gbWFrZSBzdXJlIHRoZSBNTFAgbGVhcm5zIGVmZmljaWVudGx5Lg0KDQojIyMgRmVhdHVyZSBTY2FsaW5nDQoNCk1MUHMgYXJlIHNlbnNpdGl2ZSB0byBmZWF0dXJlIHNjYWxlcywgc28gbm9ybWFsaXphdGlvbiAoZS5nLiwgTWluLU1heCBzY2FsaW5nKSBvciBzdGFuZGFyZGl6YXRpb24gKGUuZy4sIFotc2NvcmUgbm9ybWFsaXphdGlvbikgaXMgdHlwaWNhbGx5IGFwcGxpZWQgdG8gZW5zdXJlIHN0YWJsZSBncmFkaWVudCBkZXNjZW50IG9wdGltaXphdGlvbi4gDQoNCldoZXRoZXIgdGhlIHRhcmdldCB2YXJpYWJsZSBtdXN0IGJlIHJlc2NhbGVkIGluIGEgbmV1cmFsIG5ldHdvcmsgZGVwZW5kcyBvbiB0aGUgY29udGV4dCwgYnV0IGl0IGlzIG9mdGVuIHN0cm9uZ2x5IHJlY29tbWVuZGVkIGZvciBvcHRpbWFsIHBlcmZvcm1hbmNlLg0KDQpUaGUgc2NhbGluZyBvZiB0aGUgdGFyZ2V0IGlzIG5lY2Vzc2FyeSB3aGVuDQoNCiogKipPdXRwdXQgQWN0aXZhdGlvbiBDb25zdHJhaW50cyoqOiBJZiB1c2luZyBhIHNpZ21vaWQgKGJvdW5kZWQgdG8gWzAsIDFdKSBvciB0YW5oIChib3VuZGVkIHRvIFstMSwgMV0pIG91dHB1dCBhY3RpdmF0aW9uLCB0aGUgdGFyZ2V0IHZhcmlhYmxlIG11c3QgYmUgc2NhbGVkIHRvIG1hdGNoIHRoZXNlIHJhbmdlcy4NCg0KKiAqKkxvc3MgRnVuY3Rpb24gU2Vuc2l0aXZpdHkqKjogTG9zcyBmdW5jdGlvbnMgbGlrZSBNU0UgKE1lYW4gU3F1YXJlZCBFcnJvcikgYXJlIHNlbnNpdGl2ZSB0byBzY2FsZS4gTGFyZ2UgdGFyZ2V0IHZhbHVlcyBjYW4gbGVhZCB0byB1bnN0YWJsZSBncmFkaWVudHMgb3Igc2xvdyBjb252ZXJnZW5jZS4NCg0KKiAqKlJlZ3VsYXJpemF0aW9uIFRlcm1zKio6ICRMXzEkLyRMXzIkIHJlZ3VsYXJpemF0aW9uIHBlbmFsaXplcyBsYXJnZSB3ZWlnaHRzLiBVbnNjYWxlZCB0YXJnZXRzIG1heSBmb3JjZSB0aGUgbmV0d29yayB0byBsZWFybiBkaXNwcm9wb3J0aW9uYXRlbHkgbGFyZ2Ugd2VpZ2h0cyB0byBjb21wZW5zYXRlLg0KDQpXaGlsZSBub3QgYWx3YXlzIG1hbmRhdG9yeSwgKipyZXNjYWxpbmcgdGhlIHRhcmdldCB2YXJpYWJsZSoqIHR5cGljYWxseSAqKmltcHJvdmVzKiogdHJhaW5pbmcgc3RhYmlsaXR5LCBzcGVlZCwgYW5kIG1vZGVsIHBlcmZvcm1hbmNl4oCUZXNwZWNpYWxseSBmb3IgYm91bmRlZCBhY3RpdmF0aW9ucyBvciBsYXJnZS12YWx1ZSByYW5nZXMuIEFsd2F5cyB2YWxpZGF0ZSB3aXRoIHlvdXIgc3BlY2lmaWMgYXJjaGl0ZWN0dXJlIGFuZCBkYXRhLg0KDQoNCg0KIyMjIEhhbmRsaW5nIE1pc3NpbmcgRGF0YQ0KDQpIYW5kbGluZyBtaXNzaW5nIHZhbHVlcyBpbiAqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiogcmVxdWlyZXMgY2FyZWZ1bCBjb25zaWRlcmF0aW9uLCBhcyBuZXVyYWwgbmV0d29ya3MgdHlwaWNhbGx5IGV4cGVjdCBjb21wbGV0ZSBpbnB1dCBkYXRhLg0KDQpBbGwgaW1wdXRhdGlvbiBtZXRob2RzIGRpc2N1c3NlZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiBjYW4gYmUgdXNlZCB0byBwcmVwcm9jZXNzIGRhdGEgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgTUxQIG1vZGVsaW5nLiBUaGUgZ2VuZXJhbCByZWNvbW1lbmRhdGlvbiBpcyB0byB1c2Ugc3RvY2hhc3RpYyBtdWx0aXBsZSBpbXB1dGF0aW9uIHRlY2huaXF1ZXMsIHN1Y2ggYXMgTUlDRSAoTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucykgYW5kIG90aGVyIHJhbmRvbSByZXBsYWNlbWVudCBtZXRob2RzIHRoYXQgbWFrZSB1c2Ugb2YgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgdW5kZXJseWluZyBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KDQojIyMgQ2F0ZWdvcmljYWwgRW5jb2RpbmcNCg0KKipDYXRlZ29yaWNhbCBlbmNvZGluZyoqIHRyYW5zZm9ybXMgbm9uLW51bWVyaWMgdmFyaWFibGVzIGludG8gbnVtZXJpY2FsIHJlcHJlc2VudGF0aW9ucyBmb3IgbWFjaGluZSBsZWFybmluZy4gDQoNCiogRm9yICoqbm9taW5hbCBkYXRhIChubyBpbmhlcmVudCBvcmRlcikqKiwgY29tbW9uIG1ldGhvZHMgaW5jbHVkZSBvbmUtaG90IGVuY29kaW5nLCB3aGljaCBjcmVhdGVzIGJpbmFyeSBjb2x1bW5zIGZvciBlYWNoIGNhdGVnb3J5IChpZGVhbCBmb3IgPDE1IGNhdGVnb3JpZXMpLCBhbmQgdGFyZ2V0IGVuY29kaW5nLCB3aGljaCByZXBsYWNlcyBjYXRlZ29yaWVzIHdpdGggdGhlIG1lYW4gb2YgdGhlIHRhcmdldCB2YXJpYWJsZSAoYmV0dGVyIGZvciBoaWdoLWNhcmRpbmFsaXR5IGZlYXR1cmVzIGJ1dCByZXF1aXJlcyBjYXJlZnVsIHZhbGlkYXRpb24gdG8gYXZvaWQgbGVha2FnZSkuIA0KDQoqIEZvciAqKm9yZGluYWwgZGF0YSAobmF0dXJhbCBvcmRlcikqKiwgb3JkaW5hbCBlbmNvZGluZyBhc3NpZ25zIGludGVnZXJzIHdoaWxlIHByZXNlcnZpbmcgdGhlIGxvZ2ljYWwgc2VxdWVuY2UgKGUuZy4sIFNtYWxsPTEsIE1lZGl1bT0yKS4gDQoNCiogKipIaWdoLWNhcmRpbmFsaXR5IGZlYXR1cmVzKiogKGUuZy4sIFpJUCBjb2RlcykgbWF5IHVzZSBmcmVxdWVuY3kgZW5jb2RpbmcgKHJlcGxhY2luZyBjYXRlZ29yaWVzIHdpdGggdGhlaXIgb2NjdXJyZW5jZSBjb3VudHMpIG9yIGVtYmVkZGluZyBsYXllcnMgaW4gbmV1cmFsIG5ldHdvcmtzIHRvIGNhcHR1cmUgY29tcGxleCByZWxhdGlvbnNoaXBzLiANCg0KDQpUaGUgY2hvaWNlIGRlcGVuZHMgb24gdGhlIG1vZGVsIHR5cGUgYW5kIGRhdGEgY2hhcmFjdGVyaXN0aWNzOiANCg0KKiAqKlRyZWUtYmFzZWQgbW9kZWxzKiogKGUuZy4sIFJhbmRvbSBGb3Jlc3RzKSBvZnRlbiBoYW5kbGUgbGFiZWwgZW5jb2Rpbmcgd2VsbC4NCg0KKiAqKkxpbmVhciBtb2RlbHMgYW5kIG5ldXJhbCBuZXR3b3JrcyoqIHR5cGljYWxseSByZXF1aXJlIG9uZS1ob3Qgb3IgdGFyZ2V0IGVuY29kaW5nLiANCg0KQ3Jvc3MtdmFsaWRhdGlvbiBpcyBlc3NlbnRpYWwgdG8gZXZhbHVhdGUgdGhlIGltcGFjdCBvZiBlbmNvZGluZyBzdHJhdGVnaWVzIG9uIG1vZGVsIHBlcmZvcm1hbmNlLg0KDQoNCg0KIyMjIERhdGEgU3BsaXR0aW5nDQoNClByb3BlciBkYXRhIHNwbGl0dGluZyBpcyBjcnVjaWFsIGZvciB0cmFpbmluZyAqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiogdG8gZW5zdXJlIHJvYnVzdCBtb2RlbCBldmFsdWF0aW9uIGFuZCBwcmV2ZW50IG92ZXJmaXR0aW5nLiBGb3IgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEsIGEgcmFuZG9tIHNwbGl0dGluZyBpcyByZXF1aXJlZDoNCg0KKiBUd28td2F5IHNwbGl0dGluZyBmb3IgdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YSBzZXRzLiBUaGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSByYXRpbyBpcyB1c3VhbGx5IDcwJS0zMCUgb3IgODAlLTIwJS4NCg0KKiAqKk5lc3RlZCByYW5kb20gc3BsaXR0aW5nIGZvciBjcm9zcy12YWxpZGF0aW9uKiogaW52b2x2ZXMgdHdvIGxheWVycyBvZiBkYXRhIHBhcnRpdGlvbmluZzogYW4gb3V0ZXIgbG9vcCBmb3IgcGVyZm9ybWFuY2UgZXZhbHVhdGlvbiBhbmQgYW4gaW5uZXIgbG9vcCBmb3IgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nLiBUaGlzIGFwcHJvYWNoIGVuc3VyZXMgdW5iaWFzZWQgbW9kZWwgYXNzZXNzbWVudCBieSBwcmV2ZW50aW5nIGRhdGEgbGVha2FnZSBiZXR3ZWVuIHRoZSB0dW5pbmcgYW5kIGV2YWx1YXRpb24gcGhhc2VzLg0KDQpUaGVyZSBhcmUgb3RoZXIgcmFuZG9tIGRhdGEgc3BsaXR0aW5nIG1ldGhvZHMgYmFzZWQgb24gdGhlIGRhdGEgc3RydWN0dXJlcyB0aGF0IHJlcXVpcmUgc3BlY2lhbCBkZXNpZ24gc28gdGhhdCB0aGUgcmFuZG9tbHkgc3BsaXQgZGF0YSByZXRhaW4gdGhlIHNhbWUgcHJvYmFiaWxpdHkgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHNldC4NCg0KDQojIyBNTFAgQXJjaGl0ZWN0dXJlIERlc2luZw0KDQpJbiBuZXVyYWwgbmV0d29ya3MsIE1MUCAoTXVsdGlsYXllciBQZXJjZXB0cm9uKSBzaXplIGFuZCBkZXB0aCByZWZlciB0byBpdHMgYXJjaGl0ZWN0dXJlLCBwYXJ0aWN1bGFybHkgdGhlIG51bWJlciBvZiBuZXVyb25zIChzaXplKSBhbmQgdGhlIG51bWJlciBvZiBoaWRkZW4gbGF5ZXJzIChkZXB0aCkuIFRoZXNlIGZhY3RvcnMgc2lnbmlmaWNhbnRseSBpbXBhY3QgdGhlIG1vZGVsJ3MgY2FwYWNpdHksIHRyYWluaW5nIGR5bmFtaWNzLCBhbmQgZ2VuZXJhbGl6YXRpb24gYWJpbGl0eS4NCg0KKiAqKlNoYWxsb3cgbmV0d29ya3MqKiAoMS0yIGhpZGRlbiBsYXllcnMpIG1heSBzdWZmaWNlIGZvciBzaW1wbGUgdGFza3MuICAqKlNoYWxsb3cgbmV0d29ya3MqKiANCiAgKyBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24gKFVuaXZlcnNhbCBBcHByb3hpbWF0aW9uIFRoZW9yZW0pLg0KICArIG9mdGVuIHN1ZmZpY2llbnQgZm9yIHNpbXBsZSB0YXNrcyAoZS5nLiwgc21hbGwgdGFidWxhciBkYXRhc2V0cykuDQogICsgYXJlIGxlc3MgcHJvbmUgdG8gb3ZlcmZpdHRpbmcgYnV0IG1heSB1bmRlcmZpdCBjb21wbGV4IGRhdGEuDQoNCiogKipEZWVwIG5ldHdvcmtzKiogY2FuIG1vZGVsIGNvbXBsZXggcGF0dGVybnMgYnV0IHJpc2sgb3ZlcmZpdHRpbmcgYW5kIGluY3JlYXNlZCBjb21wdXRhdGlvbmFsIGNvc3QuICoqRGVlcCBuZXR3b3JrcyoqIGFyZQ0KICArIGJldHRlciBhdCBsZWFybmluZyBoaWVyYXJjaGljYWwgcmVwcmVzZW50YXRpb25zLg0KICArIHVzZWZ1bCBmb3IgaGlnaC1kaW1lbnNpb25hbCBkYXRhIChlLmcuLCBpbWFnZXMsIE5MUCBhZnRlciBmZWF0dXJlIGV4dHJhY3Rpb24pLg0KICArIG1vcmUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSBhbmQgcHJvbmUgdG8gb3ZlcmZpdHRpbmcgKHJlcXVpcmVzIHJlZ3VsYXJpemF0aW9uIGxpa2UgRHJvcG91dCwgTDIpLg0KDQoNCiogKipNTFAgU2l6ZSAoTnVtYmVyIG9mIE5ldXJvbnMgcGVyIExheWVyKSoqIGlzIGFsc28gaW1wYWN0ZnVsIG9uIHRoZSBtb2RlbCBwZXJmb3JtYW5jZS4NCiAgKyBXaWRlciBMYXllcnMgKG1vcmUgbmV1cm9ucykgaW5jcmVhc2UgbW9kZWwgY2FwYWNpdHksIGFsbG93aW5nIG1vcmUgY29tcGxleCBmdW5jdGlvbiBhcHByb3hpbWF0aW9uLiBJdCBjYW4gbGVhZCB0byBvdmVyZml0dGluZyBpZiBub3QgcmVndWxhcml6ZWQgYW5kIHVzZSBtb3JlIGNvbXB1dGF0aW9uIHJlc291cmNlcyAobW9yZSBwYXJhbWV0ZXJzKS4NCiAgKyBOYXJyb3dlciBMYXllcnMgKGZld2VyIG5ldXJvbnMpIGFyZSBsZXNzIGV4cHJlc3NpdmUgYnV0IGZhc3RlciB0byB0cmFpbiBidXQgbWF5IGxlYWQgdG8gdW5kZXJmaXR0aW5nIGNvbXBsZXggcGF0dGVybnMuDQoNCiogKipDaG9vc2luZyBEZXB0aCAmIFNpemUqKiBpcyBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIGdlbmVyYWwgcmVjb21tZW5kYXRpb25zDQogICsgU3RhcnQgd2l0aCAxLTMgaGlkZGVuIGxheWVycyBhbmQgYWRqdXN0IGJhc2VkIG9uIHBlcmZvcm1hbmNlLg0KICArIFVzZSBzaW1pbGFyIG9yIGRlY3JlYXNpbmcgbGF5ZXIgc2l6ZXMuDQogICsgV2lkZXIgbGF5ZXJzIGVhcmx5IGNhbiBoZWxwIGZlYXR1cmUgZXh0cmFjdGlvbjsgZGVlcGVyIG5ldHdvcmtzIHJlZmluZSBhYnN0cmFjdGlvbnMuDQoNCg0KIyMgQWN0aXZhdGlvbiBGdW5jdGlvbnMNCg0KKipBY3RpdmF0aW9uIGZ1bmN0aW9ucyoqIHNlcnZlIGRpc3RpbmN0IHB1cnBvc2VzIGluIGhpZGRlbiBhbmQgb3V0cHV0IGxheWVycy4gSW4gaGlkZGVuIGxheWVycywgdGhleSBpbnRyb2R1Y2Ugbm9ubGluZWFyIHRyYW5zZm9ybWF0aW9ucyB0byBjYXB0dXJlIGNvbXBsZXggcGF0dGVybnMgaW4gdGhlIGRhdGEuIFRoZSBvdXRwdXQgbGF5ZXIncyBhY3RpdmF0aW9uIGZ1bmN0aW9uIGlzIGNob3NlbiBzcGVjaWZpY2FsbHkgdG8gcHJvZHVjZSBwcmVkaWN0aW9ucyBjb21wYXRpYmxlIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSdzIGNoYXJhY3RlcmlzdGljcy4gDQoNCg0KIyMjIEhpZGRlbiBMYXllciBBY3RpdmF0aW9uDQoNClRoZXJlIGFyZSBzZXZlcmFsIGFjdGl2YXRpb24gZnVuY3Rpb25zIGZvciB0aGUgaGlkZGVuIGxheWVyLiBUaGUgUmVMVSAoUmVjdGlmaWVkIExpbmVhciBVbml0KSBpcyB3aWRlbHkgdXNlZCBkdWUgdG8gaXRzIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeSBhbmQgaXMgdGhlIGRlZmF1bHQgZm9yIG1hbnkgTUxQIGxpYnJhcmllcy4gVGhlIGZvbGxvd2luZyB0YWJsZSBvdXRsaW5lcyBzb21lIG9mIHRoZSBjb21tb25seSB1c2VkIGFjdGl2YXRpb24gZnVuY3Rpb25zIGZvciBoaWRkZW4gbGF5ZXJzLg0KDQoNCnwgQWN0aXZhdGlvbgl8IEJlc3QgRm9yCSB8ICAgIFByb3MJICB8ICAgIENvbnMgCXwgUmVjb21tZW5kYXRpb24gfA0KfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLXw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tfA0KfCBSZUxVCXwgRGVmYXVsdCBjaG9pY2UJfCBGYXN0LCBhdm9pZHMgdmFuaXNoaW5nIGdyYWRpZW50IChmb3IgeCA+IDApCXwgIkR5aW5nIFJlTFUiIChkZWFkIG5ldXJvbnMpCXwgRmlyc3QgdHJ5IGluIG1vc3QgTUxQc3wgDQp8IExlYWt5IFJlTFUJfCBEZWVwIG5ldHdvcmtzCXwgRml4ZXMgZHlpbmcgUmVMVQl8IFNsaWdodGx5IHNsb3dlciB0aGFuIFJlTFUJfCBVc2UgaWYgUmVMVSBmYWlsc3wgDQp8IEdFTFUJfCBUcmFuc2Zvcm1lcnMsIGRlZXAgbGVhcm5pbmcJfCBTbW9vdGgsIGJldHRlciBncmFkaWVudCBmbG93CXwgTW9yZSBjb21wdXRlLWhlYXZ5CXwgIEJlc3QgZm9yIG1vZGVybiBkZWVwIG5ldHMgKGUuZy4sIEJFUlQsIEdQVCl8IA0KfCBTd2lzaAl8IEV4cGVyaW1lbnRhbCBhbHRlcm5hdGl2ZQl8IENhbiBvdXRwZXJmb3JtIFJlTFUJfCBTbG93ZXIgdGhhbiBSZUxVCXwgIFRyeSBpZiB0dW5pbmcgcGVyZm9ybWFuY2V8IA0KfCBTRUxVCXwgU2VsZi1ub3JtYWxpemluZyBuZXR3b3Jrcwl8IE5vIEJhdGNoTm9ybSBuZWVkZWQJfCBSZXF1aXJlcyBjYXJlZnVsIGluaXRpYWxpemF0aW9uCXwgVXNlIG9ubHkgaW4gc3BlY2lmaWMgYXJjaGl0ZWN0dXJlc3wgDQoNCg0KPGZvbnQgY29sb3IgPSAicmVkIj4qKlxjb2xvcntyZWR9QXZvaWQgdXNpbmcgU2lnbW9pZC9UYW5oIGluIGhpZGRlbiBsYXllcnMgdGhhdCBjYXVzZSB2YW5pc2hpbmcgZ3JhZGllbnRzIGluIGRlZXAgbmV0d29ya3MuKio8L2ZvbnQ+DQoNCg0KDQojIyMgT3V0cHV0IExheWVyIEFjdGl2YXRpb24NCg0KRGVwZW5kaW5nIG9uIHRoZSB0eXBlIG9mIHRoZSB0YXJnZXQgcmVzcG9uc2UsIHRoZSBjb21tb25seSB1c2VkIGFjdGl2YXRpb24gZnVuY3Rpb25zIGFyZSBzdW1tYXJpemVkIGluIHRoZSBmb2xsb3dpbmcgdGFibGUuDQoNCnwgIFRhc2sJICAgICB8ICAgIEFjdGl2YXRpb24JICB8ICAgIFdoeT8gICAJfCAgICAgIEV4YW1wbGUgVXNlIENhc2UgICAgIHwNCnw6LS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAgUmVncmVzc2lvbiAodW5ib3VuZGVkKQl8ICBMaW5lYXIgKG5vIGFjdGl2YXRpb24pCXwgIE91dHB1dHMgYW55IHJlYWwgbnVtYmVyCXwgIFByZWRpY3RpbmcgaG91c2UgcHJpY2VzfCAgDQp8ICBSZWdyZXNzaW9uICgwIHRvIDEpCXwgIFNpZ21vaWQJfCAgQm91bmRzIG91dHB1dCB0byBbMCwgMV0JfCAgUHJlZGljdGluZyBwcm9iYWJpbGl0aWVzfCAgDQp8ICBCaW5hcnkgQ2xhc3NpZmljYXRpb24JfCAgU2lnbW9pZAl8ICBPdXRwdXRzIHByb2JhYmlsaXR5ICgwIG9yIDEpCXwgIFNwYW0gZGV0ZWN0aW9ufCAgDQp8ICBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uCXwgIFNvZnRtYXgJfCAgUHJvYmFiaWxpdGllcyBzdW0gdG8gMQl8ICBNTklTVCBkaWdpdCBjbGFzc2lmaWNhdGlvbnwgIA0KfCAgTXVsdGlsYWJlbCBDbGFzc2lmaWNhdGlvbgl8ICBTaWdtb2lkIChwZXIgY2xhc3MpCXwgIEluZGVwZW5kZW50IHByb2JhYmlsaXRpZXMJfCAgSW1hZ2UgdGFnZ2luZ3wgIA0KDQoNCjxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfU5ldmVyIHVzZSBSZUxVIGluIHRoZSBvdXRwdXQgbGF5ZXIgdW5sZXNzIG91dHB1dHMgbXVzdCBiZSDiiaUwLCBlLmcuLCBjb3VudCBwcmVkaWN0aW9uICBsaWtlIFBvaXNzb24gcmVncmVzc2lvbikuKio8L2ZvbnQ+DQoNCg0KIyMjIEd1aWRlbGluZSBmb3IgSGlkZGVuIGFuZCBPdXRwdXQgTGF5ZXJzIA0KDQoNClRoZSBmb2xsb3dpbmcgdGFibGUgcHJvdmlkZXMgYSBndWlkZWxpbmUgZm9yIGNob29zaW5nIGFwcHJvcHJpYXRlIGFjdGl2YXRpb24gZnVuY3Rpb25zIGluIE1MUC4NCg0KfCAgU2NlbmFyaW8gICB8IAlIaWRkZW4gTGF5ZXIgIHwgCU91dHB1dCBMYXllciAgfA0KfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS18DQp8ICBHZW5lcmFsIE1MUAl8ICBSZUxVCXwgIFRhc2stZGVwZW5kZW50IChzZWUgYWJvdmUpfCAgDQp8ICBEZWVwIE5ldHdvcmsJfCAgR0VMVS9MZWFreSBSZUxVIHwgIAlUYXNrLWRlcGVuZGVudHwgIA0KfCAgVHJhbnNmb3JtZXIgTW9kZWwJfCAgR0VMVQl8ICBTb2Z0bWF4L1NpZ21vaWR8ICANCnwgIFNlbGYtTm9ybWFsaXppbmcgTmV0CXwgIFNFTFUJfCAgVGFzay1kZXBlbmRlbnR8ICANCg0KDQojIyBUcmFpbmluZywgT3B0aW1pemF0aW9uIA0KDQpFZmZlY3RpdmUgdHJhaW5pbmcgcmVxdWlyZXMgcHJvcGVyIG9wdGltaXphdGlvbiB0ZWNobmlxdWVzIGFuZCBoeXBlcnBhcmFtZXRlciB0dW5pbmcuDQoNCiogKipMb3NzIEZ1bmN0aW9uIFNlbGVjdGlvbioqIGlzIGRlcGVuZGVudCBvbiB0aGUgYXBwbGljYXRpb25zOg0KICArICoqQ2xhc3NpZmljYXRpb24qKjogQ3Jvc3MtZW50cm9weSBsb3NzDQogICsgKipSZWdyZXNzaW9uKio6IE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBvciBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpDQoNCiogKipPcHRpbWl6ZXIgQ2hvaWNlKiogaXMgYWxzbyBkZXBlbmRlbnQgb24gdGhlIGFwcGxpY2F0aW9ucyBhbmQgdGhlIHJlcXVpcmVkIGNvbXB1dGF0aW9uYWwgcmVzb3VyY2VzIGZvciB0aGUgYXBwbGljYXRpb25zLg0KICArIFN0b2NoYXN0aWMgR3JhZGllbnQgRGVzY2VudCAoU0dEKTogQmFzaWMgYnV0IHJlcXVpcmVzIGNhcmVmdWwgbGVhcm5pbmcgcmF0ZSB0dW5pbmcuIA0KICArIEFkYXB0aXZlIE9wdGltaXplcnMgKEFkYW0sIFJNU3Byb3ApOiBBdXRvbWF0aWNhbGx5IGFkanVzdCBsZWFybmluZyByYXRlcywgb2Z0ZW4gbGVhZGluZyB0byBmYXN0ZXIgY29udmVyZ2VuY2UuIEEgdG9vLWhpZ2ggbGVhcm5pbmcgcmF0ZSBjYXVzZXMgaW5zdGFiaWxpdHksIHdoaWxlIGEgdG9vLWxvdyByYXRlIHNsb3dzIGNvbnZlcmdlbmNlLg0KDQoqICoqSHlwZXJwYXJhbWV0ZXIgVHVuaW5nKioNCk1ldGhvZHMgbGlrZSBncmlkIHNlYXJjaCwgcmFuZG9tIHNlYXJjaCwgb3IgQmF5ZXNpYW4gb3B0aW1pemF0aW9uIGhlbHAgZmluZCBvcHRpbWFsIGNvbmZpZ3VyYXRpb25zIGVmZmljaWVudGx5Lg0KDQpcDQoNCiMjIFJlbGF0aXZlIEltcHJvdmVtZW50IGluIFBlcmZvcm1hbmNlDQoNCldoZW4gZXZhbHVhdGluZyBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscywgaXQgaXMgZXNzZW50aWFsIHRvIHF1YW50aWZ5IGhvdyBtdWNoIGEgcHJvcG9zZWQgbmV1cmFsIG5ldHdvcmsgKE5OKSBtb2RlbCBpbXByb3ZlcyB1cG9uIGEgc2ltcGxlciBiYXNlIG1vZGVsIChlLmcuLCBsaW5lYXIgcmVncmVzc2lvbiBmb3IgcmVncmVzc2lvbiB0YXNrcywgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIGNsYXNzaWZpY2F0aW9uKS4gTWVhc3VyaW5nIHJlbGF0aXZlIGltcHJvdmVtZW50IGhlbHBzIGRldGVybWluZSB3aGV0aGVyIHRoZSBhZGRlZCBjb21wbGV4aXR5IG9mIGEgbmV1cmFsIG5ldHdvcmsganVzdGlmaWVzIGl0cyBhZG9wdGlvbi4gV2UgbmV4dCBicmllZmx5IGRpc2N1c3Mga2V5IG1ldGhvZHMgZm9yIGNvbXB1dGluZyBhbmQgaW50ZXJwcmV0aW5nIHJlbGF0aXZlIGltcHJvdmVtZW50LCBhbG9uZyB3aXRoIHByYWN0aWNhbCBjb25zaWRlcmF0aW9ucy4gUmVjYWxsIHRoYXQgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24gYXJlIGxpc3RlZC4NCg0KICArICoqQ2xhc3NpZmljYXRpb24qKjogQWNjdXJhY3ksIFByZWNpc2lvbiwgUmVjYWxsLCBGMS1zY29yZSwgUk9DLUFVQy4NCiAgKyAqKlJlZ3Jlc3Npb24qKjogTVNFLCBSTVNFLCAkUl4yJC4NCiAgDQoqKlJlbGF0aXZlIGltcHJvdmVtZW50KiogbWVhc3VyZXMgaG93IG11Y2ggYmV0dGVyIChvciB3b3JzZSkgYSBuZXVyYWwgbmV0d29yayBwZXJmb3JtcyBjb21wYXJlZCB0byBhIGJhc2VsaW5lIG1vZGVsLiBJdCBpcyB0eXBpY2FsbHkgZXhwcmVzc2VkIGFzIGEgcGVyY2VudGFnZSByZWR1Y3Rpb24gaW4gZXJyb3IgKGZvciByZWdyZXNzaW9uKSBvciBhIHBlcmNlbnRhZ2UgaW5jcmVhc2UgaW4gYWNjdXJhY3kvcHJlY2lzaW9uL3JlY2FsbCAoZm9yIGNsYXNzaWZpY2F0aW9uKS4gIFRoZSBnZW5lcmFsIGZvcm11bGEgaXMgZ2l2ZW4gYnkNCg0KJCQNClx0ZXh0e1JlbGF0aXZlIEltcHJvdmVtZW50fSA9IFxmcmFje1x0ZXh0e01ldHJpY31fe1x0ZXh0e0Jhc2V9fS1cdGV4dHtNZXRyaWN9X3tcdGV4dHtOTn19fXtcdGV4dHtNZXRyaWN9X3tcdGV4dHtCYXNlfX19DQokJA0KICANCkFzIGFuIGV4YW1wbGUsIGNvbXBhcmUgdGhlIFJNU0UgYmV0d2VlbiBsaW5lYXIgcmVncmVzc2lvbiAoZml0IGl0IHRvIHRoZSBzY2FsZWQgZGF0YSkgYW5kIE5OIG1vZGVsLiBBc3N1bWUgdGhlIGJhc2UgbW9kZWwgUk1TRSA9IDEwLjAgYW5kIE5OIG1vZGVsIFJNU0UgPSA3LiBUaGVuDQoNCiQkDQpcdGV4dHtJbXByb3ZlbWVudH0gPSBcZnJhY3sxMC03fXsxMH0gPSAzMFwlLg0KJCQNCg0KKipJbnRlcnByZXRhdGlvbioqOiBUaGUgTk4gcmVkdWNlcyBwcmVkaWN0aW9uIGVycm9yIGJ5IDMwJSBjb21wYXJlZCB0byBsaW5lYXIgcmVncmVzc2lvbi4gIA0KICANCiAgDQpcDQogIA0KIyBNTFAgUmVncmVzc2lvbg0KDQpUaGlzIHNlY3Rpb24gZm9jdXNlcyBvbiBpbXBsZW1lbnRpbmcgTUxQIHJlZ3Jlc3Npb24gdXNpbmcgdGhlIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQsIGFsb25nIHdpdGggdGhlIFIgKipuZXVyYWxuZXQqKiBsaWJyYXJ5IGFuZCBvdGhlciBzdXBwb3J0aW5nIGxpYnJhcmllcyBmb3IgZGF0YSBwcmVwYXJhdGlvbiBhbmQgdmlzdWFsaXphdGlvbi4gV2Ugd2lsbCBmb2xsb3cgdGhlIGJhc2ljIHN0ZXBzIG91dGxpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLiBXZSB3aWxsIGZpdCBvbmUtaGlkZGVuLWxheWVyIGFuZCB0d28taGlkZGVuLWxheWVyIE1MUCB0byBwcmVkaWN0IHRoZSBtZWRpYW4gaG91c2UgdmFsdWUuDQoNCldlIHVzZSAqKm1pbi1tYXggc2NhbGluZyBtZXRob2QqKiBmb3IgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMuIEFsbCBmZWF0dXJlcyBhcmUgbnVtZXJpY2FsLCBubyBjYXRlZ29yaWNhbCBlbmNvZGluZyBpcyBuZWVkZWQuIFdlIHVzZSByYW5kb20gc3BsaXR0aW5nICg3MC0zMCkgdG8gY3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEgc2V0cy4NCg0KYGBge3J9DQojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcw0KIyBsaWJyYXJ5KG5ldXJhbG5ldCkNCiMgbGlicmFyeShNQVNTKSAgICAgICAjIEZvciBCb3N0b24gSG91c2luZyBkYXRhc2V0DQojIGxpYnJhcnkoZ2dwbG90MikgICAgIyBGb3IgdmlzdWFsaXphdGlvbg0KIyBsaWJyYXJ5KGNhcmV0KSAgICAgICMgT25seSBmb3IgZGF0YSBzcGxpdHRpbmcgKHdlIHdvbid0IHVzZSBpdHMgbW9kZWxpbmcgZnVuY3Rpb25zKQ0KIyBMb2FkIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQNCmRhdGEoQm9zdG9uKQ0KDQojIENoZWNrIHN0cnVjdHVyZSBhbmQgc3VtbWFyeQ0KI3N0cihCb3N0b24pDQojc3VtbWFyeShCb3N0b24pDQoNCiMgRmVhdHVyZSBzY2FsaW5nIC0gbm9ybWFsaXplIGFsbCB2YXJpYWJsZXMgdG8gWzAsMV0gcmFuZ2UNCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7DQogIHJldHVybiAoKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQoNCmJvc3Rvbi5zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoQm9zdG9uLCBub3JtYWxpemUpKQ0KDQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMykNCk4gPC0gbGVuZ3RoKGJvc3Rvbi5zY2FsZWQkbWVkdikNCiMgQ3JlYXRlIHRyYWluLXRlc3Qgc3BsaXQgKDcwLTMwKQ0KdHJhaW4ucmVnLmluZGV4IDwtIHNhbXBsZSgxOk4sICBmbG9vcigwLjcqTiksIHJlcGxhY2UgPSBGQUxTRSkNCnRyYWluLnJlZy5kYXRhIDwtIGJvc3Rvbi5zY2FsZWRbdHJhaW4ucmVnLmluZGV4LCBdDQp0ZXN0LnJlZy5kYXRhIDwtIGJvc3Rvbi5zY2FsZWRbLXRyYWluLnJlZy5pbmRleCwgXQ0KDQojIENoZWNrIGRpbWVuc2lvbnMNCiNkaW0odHJhaW5fZGF0YSkNCiNkaW0odGVzdF9kYXRhKQ0KYGBgDQoNCiMjIE9uZS1oaWRkZW4tbGF5ZXIgUGVyY2VwdHJvbg0KDQpXZSBmaXJzdCBidWlsZCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4gV2Ugd2lsbCB0dW5lIHRocmVlIGh5cGVycGFyYW1ldGVyczogdGhlIG51bWJlciBvZiBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyLCBsZWFybmluZyByYXRlLCBhbmQgYWN0aXZhdGlvbiBmdW5jdGlvbiB1c2luZyBncmlkIHNlYXJjaC4gVGhlIHBlcmZvcm1hbmNlIG1ldHJpYyB1c2VkIHRvIHNlbGVjdCB0aGUgb3B0aW1hbCBjb21iaW5hdGlvbiBvZiB2YWx1ZXMgb2YgaHlwZXJwYXJhbWV0ZXJzIGlzIFJNU0UuDQoNCmBgYHtyfQ0KIyBEZWZpbmUgZ3JpZCBvZiBoeXBlcnBhcmFtZXRlcnMNCmh5cGVyLmdyaWQucmVnIDwtIGV4cGFuZC5ncmlkKA0KICBsYXllcjEgPSBjKDUsIDEwLCAxNSksDQogIGxlYXJuaW5nLnJhdGUgPSBjKDAuMDEsIDAuMSksDQogIGFjdGl2YXRpb24gPSBjKCJsb2dpc3RpYyIsICJ0YW5oIikNCikNCg0KIyBJbml0aWFsaXplIHJlc3VsdHMgc3RvcmFnZQ0Kcm1zZSA9IE5VTEwNCiNsYXllcjEgPSBOVUxMDQojbGVhcm5pbmdyYXRlID0gTlVMTA0KI2FjdGl2YXRpb24gPSBOVUxMDQoNCmJlc3QucmVnLnJtc2UgPC0gSW5mDQpiZXN0LnJlZy5tb2RlbCA8LSBOVUxMDQoNCiMgUGVyZm9ybSBncmlkIHNlYXJjaA0KZm9yKGkgaW4gMTpucm93KGh5cGVyLmdyaWQucmVnKSkgew0KICAjIEdldCBjdXJyZW50IGNvbmZpZ3VyYXRpb24NCiAgbGF5ZXIgPC0gaHlwZXIuZ3JpZC5yZWckbGF5ZXIxW2ldDQogIGxyIDwtIGh5cGVyLmdyaWQucmVnJGxlYXJuaW5nLnJhdGVbaV0NCiAgYWN0IDwtIGh5cGVyLmdyaWQucmVnJGFjdGl2YXRpb25baV0NCiAgDQogICMgVHJhaW4gbW9kZWwNCiAgc2V0LnNlZWQoMTIzKQ0KICBtb2RlbC5yZWcgPC0gbmV1cmFsbmV0KA0KICAgICAgbWVkdiB+IC4sDQogICAgICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogICAgICBoaWRkZW4gPSBsYXllciwNCiAgICAgIGFjdC5mY3QgPSBhY3QsDQogICAgICBsaW5lYXIub3V0cHV0ID0gVFJVRSwgICMgRm9yIHJlZ3Jlc3Npb24NCiAgICAgIGxlYXJuaW5ncmF0ZSA9IGxyLA0KICAgICAgYWxnb3JpdGhtID0gInJwcm9wKyIsDQogICAgICBzdGVwbWF4ID0gMWU1ICkNCiAgDQoNCiAgICAjIE1ha2UgcHJlZGljdGlvbnMNCiAgICBwcmVkcy5yZWcgPC0gcHJlZGljdChtb2RlbC5yZWcsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQogICAgDQogICAgIyBDYWxjdWxhdGUgUk1TRQ0KICAgIHJtc2UucmVnIDwtIHNxcnQobWVhbigocHJlZHMucmVnIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCiAgICANCiAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICBybXNlW2ldID0gcm1zZS5yZWcNCiAgICANCiAgICAjIFVwZGF0ZSBiZXN0IG1vZGVsDQogICAgaWYocm1zZS5yZWcgPCBiZXN0LnJlZy5ybXNlKSB7DQogICAgICBiZXN0LnJlZy5ybXNlIDwtIHJtc2UucmVnDQogICAgICBiZXN0LnJlZy5tb2RlbCA8LSBtb2RlbC5yZWcgDQogICAgICBiZXN0LnJlZy5wYXJhbXMgPC0gaHlwZXIuZ3JpZC5yZWdbaSwgXQ0KICAgIH0NCn0NCg0KcmVzdWx0cy5yZWdOTiA8LSBoeXBlci5ncmlkLnJlZw0KcmVzdWx0cy5yZWdOTiRybXNlIDwtIHJtc2UNCg0KDQojIFZpZXcgcmVzdWx0cyBzb3J0ZWQgYnkgUk1TRQ0KcGFuZGVyKHJlc3VsdHMucmVnTk5bb3JkZXIocmVzdWx0cy5yZWdOTiRybXNlKSwgXVsxLF0pDQpgYGANCg0KV2l0aCB0aGUgYWJvdmUgY29tYmluYXRpb24gb2Ygb3B0aW1hbCBoeXBlcnBhcmFtZXRlciB2YWx1ZXMsIHdlIHRyYWluIHRoZSBmaW5hbCBzaW5nbGUgaGlkZGVuIGxheWVyIHBlcmNlcHRyb24gbW9kZWwuDQoNCmBgYHtyfQ0KIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgd2l0aCBiZXN0IHBhcmFtZXRlcnMNCmZpbmFsLnJlZy5tb2RlbCA8LSBuZXVyYWxuZXQoDQogIG1lZHYgfiAuLA0KICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogIGhpZGRlbiA9IGJlc3QucmVnLnBhcmFtcyRsYXllcjEsDQogIGFjdC5mY3QgPSBiZXN0LnJlZy5wYXJhbXMkYWN0aXZhdGlvbiwNCiAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogIGxlYXJuaW5ncmF0ZSA9IGJlc3QucmVnLnBhcmFtcyRsZWFybmluZ19yYXRlLA0KICBhbGdvcml0aG0gPSAicnByb3ArIiwNCiAgc3RlcG1heCA9IDFlNQ0KKQ0KDQojIFBsb3QgdGhlIG5ldXJhbCBuZXR3b3JrDQojcGxvdChmaW5hbC5yZWcubW9kZWwpDQpgYGANCg0KDQoNCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjkwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvc2luZ2xlLWhpZGRlbi1NTFAtSG91c2luZy5wbmciKQ0KYGBgDQoNCg0KVGhlIGFib3ZlIE5OIHBsb3Qgc2hvd3MgdGhlIGFyY2hpdGVjdHVyZSBvZiB0aGUgZmluYWwgb25lLWhpZGRlbi1sYXllciBwZXJjZXB0cm9uIG1vZGVsLiBOZXh0LCB3ZSB3aWxsIHVzZSBpdCB0byBtYWtlIHByZWRpY3Rpb25zLg0KDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0ZXN0IHNldA0KcHJlZC5OTjEgPC0gcHJlZGljdChmaW5hbC5yZWcubW9kZWwsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCnJtc2UuTk4xIDwtIHNxcnQobWVhbigocHJlZC5OTjEgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCm1hZS5OTjEgPC0gbWVhbihhYnMocHJlZC5OTjEgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCnIuc3F1YXJlZC5OTjEgPC0gY29yKHByZWQuTk4xICwgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCiMgY2F0KCJQZXJmb3JtYW5jZSBNZXRyaWNzOlxuIikNCiMgY2F0KCJSTVNFOiIsIHJtc2UsICJcbiIpDQojIGNhdCgiTUFFOiIsIG1hZSwgIlxuIikNCiMgY2F0KCJSLXNxdWFyZWQ6Iiwgcl9zcXVhcmVkLCAiXG4iKQ0KDQojIFBsb3QgcHJlZGljdGlvbnMgdnMgYWN0dWFsDQpwbG90Lk5OMS5kYXRhIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgUHJlZGljdGVkID0gcHJlZC5OTjEgDQopDQoNCmdncGxvdChwbG90Lk5OMS5kYXRhLCBhZXMoeCA9IEFjdHVhbCwgeSA9IFByZWRpY3RlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJkYXJrcmVkIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MC44NSwgeT0uMiwgDQogICAgICAgICAgIGxhYmVsPXBhc3RlKCJSLnNxID0iLCByb3VuZChyLnNxdWFyZWQuTk4xLDQpKSwgY29sb3I9ImJsdWUiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeD0wLjg1LCB5PS4xMywgDQogICAgICAgICAgIGxhYmVsPXBhc3RlKCJSTVNFID0iLCByb3VuZChybXNlLk5OMSw0KSksIGNvbG9yPSJibHVlIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MC44NSwgeT0uMDYsIA0KICAgICAgICAgICBsYWJlbD1wYXN0ZSgiICBNQUUgPSIsIHJvdW5kKG1hZS5OTjEsNCkpLCBjb2xvcj0iYmx1ZSIpICsNCiAgZ2d0aXRsZSgiQWN0dWFsIHZzIFByZWRpY3RlZCBWYWx1ZXMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClRoZSBmaWd1cmUgYWJvdmUgZGVtb25zdHJhdGVzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHByZWRpY3RlZCB0YXJnZXQgdmFsdWVzIGFuZCB0aGUgc2NhbGVkIHRhcmdldCB2YWx1ZXMuIEJvdGggdGhlIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBhbmQgbWVhbiBhYnNvbHV0ZSBlcnJvciAoTUFFKSBtZXRyaWNzIGFyZSBkaXNwbGF5ZWQgb24gdGhlIHBsb3QuIE5leHQsIHdlIGZpdCBhIGNsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB0byB0aGUgc2NhbGVkIGRhdGEgYW5kIHBlcmZvcm0gYSBjb21wYXJhdGl2ZSBhbmFseXNpcyBiZXR3ZWVuIHRoaXMgYmFzZWxpbmUgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIG91ciBuZXVyYWwgbmV0d29yayBtb2RlbC4NCg0KDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KGdncGxvdDIpDQojIFRyYWluIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQpsbS5tb2RlbCA8LSBsbShtZWR2IH4gLiwgZGF0YSA9IHRlc3QucmVnLmRhdGEpDQoNCiMgTWFrZSBwcmVkaWN0aW9ucw0KbG0ucHJlZGljdGlvbnMgPC0gcHJlZGljdChsbS5tb2RlbCwgdGVzdC5yZWcuZGF0YVssIC1uY29sKHRlc3QucmVnLmRhdGEpXSkNCg0KIyBDYWxjdWxhdGUgcGVyZm9ybWFuY2UgbWV0cmljcw0KbG0ucm1zZSA8LSBzcXJ0KG1lYW4oKGxtLnByZWRpY3Rpb25zIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCmxtLm1hZSA8LSBtZWFuKGFicyhsbS5wcmVkaWN0aW9ucyAtIHRlc3QucmVnLmRhdGEkbWVkdikpDQpsbS5yLnNxdWFyZWQgPC0gY29yKGxtLnByZWRpY3Rpb25zLCB0ZXN0LnJlZy5kYXRhJG1lZHYpXjINCg0KDQojIyBpbXByb3ZlbWVudHMNClJNU0UuaW1wIDwtIHJvdW5kKChsbS5ybXNlIC0gcm1zZS5OTjEpL2xtLnJtc2UgKiAxMDAsMikNCk1BRS5pbXAgPC0gcm91bmQoKGxtLm1hZSAtIG1hZS5OTjEpL2xtLm1hZSAqIDEwMCwgMikNClJzcS5pbXAgPC0gcm91bmQoKHIuc3F1YXJlZC5OTjEgLSBsbS5yLnNxdWFyZWQpL2xtLnIuc3F1YXJlZCAqIDEwMCwyKQ0KDQojIw0KUGVyZm9ybWFuY2UudGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgTE0gPSBjKGxtLnJtc2UsIGxtLm1hZSwgbG0uci5zcXVhcmVkKSwNCiAgTk4uMSA9IGMocm1zZS5OTjEsIG1hZS5OTjEsIHIuc3F1YXJlZC5OTjEpLA0KICBJbXByb3ZlbWVudC5wZXJjZW50YWdlID0gYyhSTVNFLmltcCwgTUFFLmltcCwgUnNxLmltcCkNCikNCnJvd25hbWVzKFBlcmZvcm1hbmNlLnRhYmxlKSA8LSBjKCJSTVNFIiwgIk1BRSIsICJSLnNxdWFyZSIpDQpwYW5kZXIoUGVyZm9ybWFuY2UudGFibGUpDQpgYGANCg0KDQpgYGB7cn0NCiMgUGxvdCBib3RoIHByZWRpY3Rpb25zDQpjb21wYXJpc29uLmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgQWN0dWFsID0gdGVzdC5yZWcuZGF0YSRtZWR2LA0KICBNTFAgPSBwcmVkLk5OMSwNCiAgTGluZWFyID0gbG0ucHJlZGljdGlvbnMNCikNCg0KZ2dwbG90KGNvbXBhcmlzb24uZGF0YSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTUxQLCBjb2xvciA9ICJNTFAiKSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTGluZWFyLCBjb2xvciA9ICJMaW5lYXIgUmVncmVzc2lvbiIpKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJNTFAiID0gImJsdWUiLCAiTGluZWFyIFJlZ3Jlc3Npb24iID0gInJlZCIpKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWwgQ29tcGFyaXNvbjogQWN0dWFsIHZzIFByZWRpY3RlZCIsDQogICAgICAgeCA9ICJBY3R1YWwgVmFsdWVzIiwNCiAgICAgICB5ID0gIlByZWRpY3RlZCBWYWx1ZXMiLA0KICAgICAgIGNvbG9yID0gIk1vZGVsIFR5cGUiKSArDQogIHRoZW1lKA0KICAgIHBsb3QubWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDQwLCAyMCwgMjAsIDIwLCB1bml0ID0gInB0IiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMS4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxMCkNCiAgICApDQpgYGANCg0KVGhlIGFib3ZlIHNjYXR0ZXIgcGxvdCBvZiB0aGUgdHJ1ZSB0YXJnZXQgdmFsdWVzIGFuZCBwcmVkaWN0ZWQgdmFsdWVzIGJhc2VkIG9uIHRoZSB0d28gbW9kZWxzIGFsc28gc2hvd3MgdGhhdCB0aGUgb25lLWhpZGRlbi1wZXJjZXB0cm9uIG1vZGVsIG91dHBlcmZvcm1zIHRoZSBjbGFzc2ljIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLg0KDQoNCiMjIFR3by1oaWRkZW4tbGF5ZXIgUGVyY2VwdHJvbg0KDQpUaGlzIHN1YnNlY3Rpb24gZXhwbG9yZXMgd2hldGhlciBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdGlvbiB0aGF0IGhhcyBtb3JlIGNvbXBsZXggYXJjaGl0ZWN0dXJlIGNvdWxkIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlLg0KDQpUaGUgbW9kZWwtYnVpbGRpbmcgcHJvY2VzcyBpcyBpZGVudGljYWwgdG8gdGhlIHByZXZpb3VzIG9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4gV2Ugd2lsbCBub3QgZGV0YWlsIHRoZSBzdGVwcyBhcyB3ZSBkaWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCmBgYHtyfQ0KIyBEZWZpbmUgZ3JpZCBvZiBoeXBlcnBhcmFtZXRlcnMNCmh5cGVyLmdyaWQuTk4yIDwtIGV4cGFuZC5ncmlkKA0KICBsYXllcjEgPSBjKDUsIDEwLCAxNSksDQogIGxheWVyMiA9IGMoMCwgMywgNSksICAjIDAgbWVhbnMgbm8gc2Vjb25kIGxheWVyDQogIGxlYXJuaW5nLnJhdGUgPSBjKDAuMDEsIDAuMSksDQogIGFjdGl2YXRpb24gPSBjKCJsb2dpc3RpYyIsICJ0YW5oIikNCikNCg0KIyBJbml0aWFsaXplIHJlc3VsdHMgaW4gc3RvcmFnZQ0KcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkNCmJlc3Qucm1zZSA8LSBJbmYNCmJlc3QubW9kZWwgPC0gTlVMTA0KDQojIFBlcmZvcm0gZ3JpZCBzZWFyY2gNCmZvcihpIGluIDE6bnJvdyhoeXBlci5ncmlkLk5OMikpIHsNCiAgIyBHZXQgY3VycmVudCBjb25maWd1cmF0aW9uDQogIGxheWVyMSA8LSBoeXBlci5ncmlkLk5OMiRsYXllcjFbaV0NCiAgbGF5ZXIyIDwtIGh5cGVyLmdyaWQuTk4yJGxheWVyMltpXQ0KICBsciA8LSBoeXBlci5ncmlkLk5OMiRsZWFybmluZy5yYXRlW2ldDQogIGFjdCA8LSBoeXBlci5ncmlkLk5OMiRhY3RpdmF0aW9uW2ldDQogIA0KICAjIENyZWF0ZSBoaWRkZW4gbGF5ZXJzIHZlY3Rvcg0KICBpZihsYXllcjIgPT0gMCkgew0KICAgIGhpZGRlbi5sYXllcnMgPC0gYyhsYXllcjEpDQogIH0gZWxzZSB7DQogICAgaGlkZGVuLmxheWVycyA8LSBjKGxheWVyMSwgbGF5ZXIyKQ0KICB9DQogIA0KICAjIFRyYWluIG1vZGVsDQogIHNldC5zZWVkKDEyMykNCiAgbW9kZWwuTk4yIDwtIHRyeUNhdGNoKHsNCiAgICBuZXVyYWxuZXQoDQogICAgICBtZWR2IH4gLiwNCiAgICAgIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgICAgIGhpZGRlbiA9IGhpZGRlbi5sYXllcnMsDQogICAgICBhY3QuZmN0ID0gYWN0LA0KICAgICAgbGluZWFyLm91dHB1dCA9IFRSVUUsICAjIEZvciByZWdyZXNzaW9uDQogICAgICBsZWFybmluZ3JhdGUgPSBsciwNCiAgICAgIGFsZ29yaXRobSA9ICJycHJvcCsiLA0KICAgICAgc3RlcG1heCA9IDFlNQ0KICAgICkNCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQ0KICANCiAgaWYoIWlzLm51bGwobW9kZWwuTk4yKSkgew0KICAgICMgTWFrZSBwcmVkaWN0aW9ucw0KICAgIHByZWRzIDwtIHByZWRpY3QobW9kZWwuTk4yLCB0ZXN0LnJlZy5kYXRhWywgLW5jb2wodGVzdC5yZWcuZGF0YSldKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIFJNU0UNCiAgICBybXNlIDwtIHNxcnQobWVhbigocHJlZHMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KICAgIA0KICAgICMgU3RvcmUgcmVzdWx0cw0KICAgIHJlc3VsdHMgPC0gcmJpbmQocmVzdWx0cywgZGF0YS5mcmFtZSgNCiAgICAgIGxheWVyMSA9IGxheWVyMSwNCiAgICAgIGxheWVyMiA9IGxheWVyMiwNCiAgICAgIGxlYXJuaW5nX3JhdGUgPSBsciwNCiAgICAgIGFjdGl2YXRpb24gPSBhY3QsDQogICAgICBybXNlID0gcm1zZQ0KICAgICkpDQogICAgDQogICAgIyBVcGRhdGUgYmVzdCBtb2RlbA0KICAgIGlmKHJtc2UgPCBiZXN0LnJtc2UpIHsNCiAgICAgIGJlc3Qucm1zZSA8LSBybXNlDQogICAgICBiZXN0Lm1vZGVsIDwtIG1vZGVsLk5OMg0KICAgICAgYmVzdC5wYXJhbXMgPC0gaHlwZXIuZ3JpZC5OTjJbaSwgXQ0KICAgIH0NCiAgfQ0KfQ0KDQojIFZpZXcgcmVzdWx0cyBzb3J0ZWQgYnkgUk1TRQ0KcmVzdWx0c1tvcmRlcihyZXN1bHRzJHJtc2UpLCBdDQpgYGANCg0KYGBge3J9DQojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB3aXRoIGJlc3QgcGFyYW1ldGVycw0KZmluYWwubW9kZWwuTk4yIDwtIG5ldXJhbG5ldCgNCiAgbWVkdiB+IC4sDQogIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgaGlkZGVuID0gaWYoYmVzdC5wYXJhbXMkbGF5ZXIyID09IDApIHsNCiAgICAgICAgYyhiZXN0LnBhcmFtcyRsYXllcjEpfSBlbHNlIHsNCiAgICAgICAgYyhiZXN0LnBhcmFtcyRsYXllcjEsIGJlc3QucGFyYW1zJGxheWVyMil9LA0KICBhY3QuZmN0ID0gYmVzdC5wYXJhbXMkYWN0aXZhdGlvbiwNCiAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogIGxlYXJuaW5ncmF0ZSA9IGJlc3QucGFyYW1zJGxlYXJuaW5nLnJhdGUsDQogIGFsZ29yaXRobSA9ICJycHJvcCsiLA0KICBzdGVwbWF4ID0gMWU1DQopDQoNCiMgUGxvdCB0aGUgbmV1cmFsIG5ldHdvcmsNCiNwbG90KGZpbmFsLm1vZGVsLk5OMikNCmBgYA0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3R3by1oaWRkZW4tTUxQLUhvdXNpbmcucG5nIikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0ZXN0IHNldA0KcHJlZC5OTjIgPC0gcHJlZGljdChmaW5hbC5tb2RlbC5OTjIsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCnJtc2UuTk4yIDwtIHNxcnQobWVhbigocHJlZC5OTjIgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCm1hZS5OTjIgPC0gbWVhbihhYnMocHJlZC5OTjIgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCnIuc3F1YXJlZC5OTjIgPC0gY29yKHByZWQuTk4yICwgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCiMjIHZlY3RvciBvZiBlcnJvciBtZXRyaWMNCk5OMiA8LSBjKHJtc2UuTk4yLCBtYWUuTk4yLCByLnNxdWFyZWQuTk4yKQ0KDQojIFBsb3QgcHJlZGljdGlvbnMgdnMgYWN0dWFsDQpwbG90LmRhdGEuTk4yIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgUHJlZGljdGVkID0gcHJlZC5OTjIgDQopDQoNCmdncGxvdChwbG90LmRhdGEuTk4yLCBhZXMoeCA9IEFjdHVhbCwgeSA9IFByZWRpY3RlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiKSArDQogIGdndGl0bGUoIkFjdHVhbCB2cyBQcmVkaWN0ZWQgVmFsdWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KIyBUcmFpbiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbA0KbG0ubW9kZWwgPC0gbG0obWVkdiB+IC4sIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSkNCg0KIyBNYWtlIHByZWRpY3Rpb25zDQpsbS5wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGxtLm1vZGVsLCB0ZXN0LnJlZy5kYXRhWywgLW5jb2wodGVzdC5yZWcuZGF0YSldKQ0KDQojIENhbGN1bGF0ZSBwZXJmb3JtYW5jZSBtZXRyaWNzDQpsbS5ybXNlIDwtIHNxcnQobWVhbigobG0ucHJlZGljdGlvbnMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KbG0ubWFlIDwtIG1lYW4oYWJzKGxtLnByZWRpY3Rpb25zIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCmxtLnIuc3F1YXJlZCA8LSBjb3IobG0ucHJlZGljdGlvbnMsIHRlc3QucmVnLmRhdGEkbWVkdileMg0KDQojIyMNCnJtc2UuTk4yLmltcCA8LSAobG0ucm1zZSAtIHJtc2UuTk4yKS9sbS5ybXNlKjEwMA0KbWFlLk5OMi5pbXAgPC0gKGxtLm1hZSAtIG1hZS5OTjIpL2xtLm1hZSAqIDEwMA0KUnNxLk5OMi5pbXAgPC0gKHIuc3F1YXJlZC5OTjIgLSBsbS5yLnNxdWFyZWQpL2xtLnIuc3F1YXJlZCAqIDEwMA0KDQpOTjIuaW1wcm92ZSA8LWMocm1zZS5OTjIuaW1wLCBtYWUuTk4yLmltcCwgUnNxLk5OMi5pbXApDQoNCnBlcmYubWF0cml4IDwtZGF0YS5mcmFtZSgNCiAgICAgICAgICAgICAgTE0gPSBjKGxtLnJtc2UsIGxtLm1hZSwgbG0uci5zcXVhcmVkKSwNCiAgICAgICAgICAgICAgTk4uMSA9IGMocm1zZS5OTjEsIG1hZS5OTjEsIHIuc3F1YXJlZC5OTjEpLA0KICAgICAgICAgICAgICBOTi4yID0gYyhybXNlLk5OMiwgbWFlLk5OMiwgci5zcXVhcmVkLk5OMikNCiAgICAgICAgICkNCg0KcGVyZi5tYXRyaXgkTk4xLkltcHJvdmUgPC0gcm91bmQoMTAwKihwZXJmLm1hdHJpeCRMTS1wZXJmLm1hdHJpeCROTi4xKS9wZXJmLm1hdHJpeCRMTSwyKQ0KcGVyZi5tYXRyaXgkTk4yLkltcHJvdmUgPC0gcm91bmQoMTAwKihwZXJmLm1hdHJpeCRMTS1wZXJmLm1hdHJpeCROTi4yKS9wZXJmLm1hdHJpeCRMTSwyKQ0KDQpyb3duYW1lcyhwZXJmLm1hdHJpeCkgPC0gYygiUk1TRSIsICJNQUUiLCAiUi5zcSIpDQpwYW5kZXIocGVyZi5tYXRyaXgpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIFBsb3QgYm90aCBwcmVkaWN0aW9ucw0KY29tcGFyaXNvbi5kYXRhLk5OMiA8LSBkYXRhLmZyYW1lKA0KICBBY3R1YWwgPSB0ZXN0LnJlZy5kYXRhJG1lZHYsDQogIE1MUCA9IHByZWQuTk4yICwNCiAgTGluZWFyID0gbG0ucHJlZGljdGlvbnMNCikNCg0KZ2dwbG90KGNvbXBhcmlzb24uZGF0YS5OTjIpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IE1MUCwgY29sb3IgPSAiTUxQIikpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IExpbmVhciwgY29sb3IgPSAiTGluZWFyIFJlZ3Jlc3Npb24iKSkgKw0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTUxQIiA9ICJibHVlIiwgIkxpbmVhciBSZWdyZXNzaW9uIiA9ICJyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsIENvbXBhcmlzb246IEFjdHVhbCB2cyBQcmVkaWN0ZWQiLA0KICAgICAgIHggPSAiQWN0dWFsIFZhbHVlcyIsDQogICAgICAgeSA9ICJQcmVkaWN0ZWQgVmFsdWVzIiwNCiAgICAgICBjb2xvciA9ICJNb2RlbCBUeXBlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMgTUxQIENsYXNzaWZpY2F0aW9uDQoNClRoZSBtb2RlbGluZyBwcm9jZXNzIGZvciBNTFAgY2xhc3NpZmljYXRpb24gaXMgaWRlbnRpY2FsIHRvIHRoYXQgb2YgTUxQIHJlZ3Jlc3Npb24uIEFzIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiBvbiBNTFAgcmVncmVzc2lvbiwgd2Ugd2lsbCBkb2N1bWVudCB0aGUgbW9kZWxpbmcgcHJvY2VzcyBpbiBkZXRhaWwuDQoNClRoZSAqKlBpbWEgSW5kaWFuIERpYWJldGVzKiogZGF0YXNldCBjb250YWlucyBvbmx5IG51bWVyaWNhbCBmZWF0dXJlIHZhcmlhYmxlcy4gVGhlcmVmb3JlLCB3ZSB3aWxsIGFwcGx5IG1pbi1tYXggc2NhbGluZyB0byBhbGwgZmVhdHVyZXMgYW5kIGNvbnZlcnQgdGhlIHRhcmdldCB2YXJpYWJsZSBpbnRvIGEgZmFjdG9yIHZhcmlhYmxlLg0KDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCiMgbGlicmFyeShuZXVyYWxuZXQpDQojIGxpYnJhcnkocFJPQykgICAgICMgRm9yIFJPQyBhbmFseXNpcw0KIyBsaWJyYXJ5KGdncGxvdDIpICAjIEZvciB2aXN1YWxpemF0aW9uDQoNCiMgTG9hZCB0aGUgUGltYSBJbmRpYW5zIERpYWJldGVzIGRhdGFzZXQNCmRhdGEoIlBpbWFJbmRpYW5zRGlhYmV0ZXMyIiwgcGFja2FnZSA9ICJtbGJlbmNoIikNCg0KIyMgcmVtb3ZpbmcgcmVjb3JkcyB3aXRoIG1pc3NpbmcgY29tcG9uZW50OyBpbXB1dGF0aW9uIHNob3VsZCBiZSBjb25zaWRlcmVkIGluDQojIyBwcmFjdGljYWwgYXBwbGljYXRpb25zDQpkaWFiZXRlcy5kYXRhIDwtIG5hLm9taXQoUGltYUluZGlhbnNEaWFiZXRlczIpICANCg0KIyBGZWF0dXJlIHNjYWxpbmcgLSBub3JtYWxpemUgbnVtZXJpYyB2YXJpYWJsZXMgdG8gWzAsMV0gcmFuZ2UNCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7DQogIHJldHVybiAoKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQojIEFwcGx5IG5vcm1hbGl6YXRpb24gdG8gYWxsIG51bWVyaWMgY29sdW1ucw0KbnVtZXJpYy5jb2xzIDwtIHNhcHBseShkaWFiZXRlcy5kYXRhLCBpcy5udW1lcmljKQ0KZGlhYmV0ZXMuZGF0YVtudW1lcmljLmNvbHNdIDwtIGxhcHBseShkaWFiZXRlcy5kYXRhW251bWVyaWMuY29sc10sIG5vcm1hbGl6ZSkNCg0KIyBFbmNvZGUgdGhlIHRhcmdldCB2YXJpYWJsZSAoZGlhYmV0ZXMpIGFzIG51bWVyaWMgKDAvMSkNCmRpYWJldGVzLmRhdGEkZGlhYmV0ZXMgPC0gaWZlbHNlKGRpYWJldGVzLmRhdGEkZGlhYmV0ZXMgPT0gInBvcyIsIDEsIDApDQoNCiMgVHdvLXdheSBkYXRhIHNwbGl0dGluZzogNzAtMzAlDQpzZXQuc2VlZCgxMjMpICAjIEZvciByZXByb2R1Y2liaWxpdHkNCnNhbXBsZS5zaXplLmNscyA8LSBmbG9vcigwLjcwICogbnJvdyhkaWFiZXRlcy5kYXRhKSkNCnRyYWluLmluZGljZXMuY2xzIDwtIHNhbXBsZSgxOnNhbXBsZS5zaXplLmNscywgc2l6ZSA9IHNhbXBsZS5zaXplLmNscywgcmVwbGFjZSA9IEZBTFNFKQ0KDQp0cmFpbi5kYXRhLmNscyA8LSBkaWFiZXRlcy5kYXRhW3RyYWluLmluZGljZXMuY2xzLCBdDQp0ZXN0LmRhdGEuY2xzIDwtIGRpYWJldGVzLmRhdGFbLXRyYWluLmluZGljZXMuY2xzLCBdDQpgYGANCg0KDQpUbyBzaW1wbGlmeSBoeXBlcnBhcmFtZXRlciB0dW5pbmcgYW5kIGZpbmFsIG1vZGVsIHRyYWluaW5nIHdpdGggdGhlIHByZS1zZWxlY3RlZCBNTFAgYXJjaGl0ZWN0dXJlIGZvciBjbGFzc2lmaWNhdGlvbiwgd2UgZGVmaW5lIGEgY3VzdG9tIGZ1bmN0aW9uIHRvIGRldGVybWluZSB0aGUgb3B0aW1hbCBudW1iZXIgb2Ygbm9kZXMgZm9yIGJvdGggc2luZ2xlLWhpZGRlbi1sYXllciBhbmQgZG91YmxlLWhpZGRlbi1sYXllciBNTFBzLiBUaGUgY3JpdGVyaW9uIGZvciBzZWxlY3RpbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzIGlzIHRoZSBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgKEFVQykgYXMgdGhlIGV2YWx1YXRpb24gbWV0cmljLCB0aG91Z2ggYWNjdXJhY3kgYmFzZWQgb24gdGhlIGRlZmF1bHQgMC41IHRocmVzaG9sZHMgY291bGQgYWx0ZXJuYXRpdmVseSBiZSB1c2VkLiBXZSBjaG9vc2UgdGhlIGxvZ2lzdGljIGFjdGl2YXRpb24gZnVuY3Rpb24gd2hpbGUga2VlcGluZyBhbGwgb3RoZXIgaHlwZXJwYXJhbWV0ZXJzIGF0IHRoZWlyIGRlZmF1bHQgdmFsdWVzLg0KDQoNCmBgYHtyfQ0KIyBGdW5jdGlvbiB0byBwZXJmb3JtIGdyaWQgc2VhcmNoIGZvciBuZXVyYWxuZXQNCm5ldXJhbG5ldC5ncmlkLnNlYXJjaCA8LSBmdW5jdGlvbih0cmFpbi5kYXRhLCB0ZXN0LmRhdGEsIGhpZGRlbi5sYXllcnMgPSAxKSB7DQogICMgRGVmaW5lIHRoZSBncmlkIG9mIGh5cGVycGFyYW1ldGVycw0KICBpZiAoaGlkZGVuLmxheWVycyA9PSAxKSB7DQogICAgaGlkZGVuLm5vZGVzIDwtIGMoMiwgNCwgNiwgOCwgMTApDQogICAgZ3JpZCA8LSBleHBhbmQuZ3JpZChoaWRkZW4gPSBoaWRkZW4ubm9kZXMpDQogIH0gZWxzZSB7DQogICAgaGlkZGVuLm5vZGVzIDwtIGMoMiwgNCwgNikNCiAgICBncmlkIDwtIGV4cGFuZC5ncmlkKGhpZGRlbjEgPSBoaWRkZW4ubm9kZXMsIGhpZGRlbjIgPSBoaWRkZW4ubm9kZXMpDQogIH0NCiAgDQogICMgQWRkIGNvbHVtbnMgdG8gc3RvcmUgcmVzdWx0cw0KICBncmlkJGFjY3VyYWN5IDwtIE5BDQogIGdyaWQkYXVjIDwtIE5BDQogIA0KICAjIEZvcm11bGEgZm9yIG5ldXJhbCBuZXR3b3JrDQogIG5uLmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhYmV0ZXMgfiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyh0cmFpbi5kYXRhKVshbmFtZXModHJhaW4uZGF0YSkgJWluJSAiZGlhYmV0ZXMiXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiArICIpKSkNCiAgDQogICMgUGVyZm9ybSBncmlkIHNlYXJjaA0KICBmb3IgKGkgaW4gMTpucm93KGdyaWQpKSB7DQogICAgaWYgKGhpZGRlbi5sYXllcnMgPT0gMSkgew0KICAgICAgaGlkZGVuIDwtIGMoZ3JpZCRoaWRkZW5baV0pDQogICAgfSBlbHNlIHsNCiAgICAgIGhpZGRlbiA8LSBjKGdyaWQkaGlkZGVuMVtpXSwgZ3JpZCRoaWRkZW4yW2ldKQ0KICAgIH0NCiAgICANCiAgICAjIFRyYWluIHRoZSBtb2RlbA0KICAgIG5uLm1vZGVsIDwtIG5ldXJhbG5ldCgNCiAgICAgIGZvcm11bGEgPSBubi5mb3JtdWxhLA0KICAgICAgZGF0YSA9IHRyYWluLmRhdGEsDQogICAgICBoaWRkZW4gPSBoaWRkZW4sDQogICAgICBsaW5lYXIub3V0cHV0ID0gRkFMU0UsICAjIEZvciBjbGFzc2lmaWNhdGlvbg0KICAgICAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsICAgIyBTaWdtb2lkIGFjdGl2YXRpb24NCiAgICAgIHN0ZXBtYXggPSAxZTYgICAgICAgICAgICMgSW5jcmVhc2UgbWF4IHN0ZXBzIGZvciBjb252ZXJnZW5jZQ0KICAgICkNCiAgICANCiAgICAjIE1ha2UgcHJlZGljdGlvbnMNCiAgICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG5uLm1vZGVsLCB0ZXN0LmRhdGEpDQogICAgcHJlZGljdGVkLmNsYXNzZXMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID4gMC41LCAxLCAwKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIGFjY3VyYWN5DQogICAgYWNjdXJhY3kgPC0gbWVhbihwcmVkaWN0ZWQuY2xhc3NlcyA9PSB0ZXN0LmRhdGEkZGlhYmV0ZXMpDQogICAgDQogICAgIyBDYWxjdWxhdGUgQVVDDQogICAgcm9jLm9iaiA8LSByb2ModGVzdC5kYXRhJGRpYWJldGVzLCBwcmVkaWN0aW9ucykNCiAgICBhdWMudmFsdWUgPC0gYXVjKHJvYy5vYmopDQogICAgDQogICAgIyBTdG9yZSByZXN1bHRzDQogICAgZ3JpZCRhY2N1cmFjeVtpXSA8LSBhY2N1cmFjeQ0KICAgIGdyaWQkYXVjW2ldIDwtIGF1Yy52YWx1ZQ0KICB9DQogIHJldHVybihncmlkKQ0KfQ0KYGBgDQoNCg0KVGhlIHBlcmZvcm1hbmNlIHRhYmxlIG9mIGNvcnJlc3BvbmRpbmcgbnVtYmVyIG9mIG5vZGVzIGluIG9uZS1oaWRkZW4tbGF5ZXIgTUxQIGlzIGdpdmVuIGJlbG93Lg0KDQpgYGB7cn0NCiMgUGVyZm9ybSBncmlkIHNlYXJjaCBmb3Igc2luZ2xlIGhpZGRlbiBsYXllcg0KZ3JpZC5yZXN1bHRzLjFsYXllciA8LSBuZXVyYWxuZXQuZ3JpZC5zZWFyY2godHJhaW4uZGF0YT10cmFpbi5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LmRhdGE9dGVzdC5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4ubGF5ZXJzID0gMSkNCnBhbmRlcihncmlkLnJlc3VsdHMuMWxheWVyKQ0KYGBgDQoNClRoZSBvcHRpbWFsIG51bWJlciBvZiBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyIGlzIHRoZSBjb3JyZXNwb25kcyB0byB0aGUgc21hbGxlc3QgQVVDLiBTaW1pbGFybHksIHRoZSBwZXJmb3JtYW5jZSB0YWJsZSBvZiB0d28taGlkZGVuLWxheWVyIE1MUCBpcyBnaXZlbiBiZWxvdy4gDQoNCmBgYHtyfQ0KIyBQZXJmb3JtIGdyaWQgc2VhcmNoIGZvciB0d28gaGlkZGVuIGxheWVycw0KZ3JpZC5yZXN1bHRzLjJsYXllciA8LSBuZXVyYWxuZXQuZ3JpZC5zZWFyY2godHJhaW4uZGF0YT10cmFpbi5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LmRhdGE9dGVzdC5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4ubGF5ZXJzID0gMikNCnBhbmRlcihncmlkLnJlc3VsdHMuMmxheWVyKQ0KYGBgDQoNCioqT25lLWhpZGRlbi1sYXllciBNTFAqKg0KDQpXZSB1c2UgdGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzIHRvIGZpdHRoZSBvbmVoaWRkZW4tbGF5ZXIgTUxQIHRvIHRoZSBkYXRhIGFuZiBvYnRhaW4NCg0KDQpgYGB7cn0NCiMgRm9ybXVsYSBmb3IgbmV1cmFsIG5ldHdvcmsNCm5uLmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhYmV0ZXMgfiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXModHJhaW4uZGF0YS5jbHMpWyFuYW1lcyh0cmFpbi5kYXRhLmNscykgJWluJSAiZGlhYmV0ZXMiXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgKyAiKSkpDQoNCiMgVHJhaW4gc2luZ2xlIGhpZGRlbiBsYXllciBtb2RlbCAodXNpbmcgYmVzdCBjb25maWd1cmF0aW9uIGZyb20gZ3JpZCBzZWFyY2gpDQpiZXN0LjFsYXllciA8LSBncmlkLnJlc3VsdHMuMWxheWVyW3doaWNoLm1heChncmlkLnJlc3VsdHMuMWxheWVyJGF1YyksIF0NCg0Kbm4uMWxheWVyIDwtIG5ldXJhbG5ldCgNCiAgZm9ybXVsYSA9IG5uLmZvcm11bGEsDQogIGRhdGEgPSB0cmFpbi5kYXRhLmNscywNCiAgaGlkZGVuID0gYmVzdC4xbGF5ZXIkaGlkZGVuLA0KICBsaW5lYXIub3V0cHV0ID0gRkFMU0UsDQogIGFjdC5mY3QgPSAibG9naXN0aWMiLA0KICBzdGVwbWF4ID0gMWU2DQopDQojIw0KI3Bsb3Qobm4uMWxheWVyLCBtYWluID0gcGFzdGUoIk9uZS1oaWRkZW4tbGF5ZXIgd2l0aCIsIGJlc3QuMWxheWVyJGhpZGRlbiwgIk5vZGVzIikpDQpgYGANCg0KYGBge3IgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9zaW5nbGUtaGlkZGVuLU1MUC1EaWFiZXRlcy5wbmciKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgVHJhaW4gdHdvIGhpZGRlbiBsYXllcnMgbW9kZWwgKHVzaW5nIGJlc3QgY29uZmlndXJhdGlvbiBmcm9tIGdyaWQgc2VhcmNoKQ0KYmVzdC4ybGF5ZXIgPC0gZ3JpZC5yZXN1bHRzLjJsYXllclt3aGljaC5tYXgoZ3JpZC5yZXN1bHRzLjJsYXllciRhdWMpLCBdDQoNCm5uLjJsYXllciA8LSBuZXVyYWxuZXQoDQogIGZvcm11bGEgPSBubi5mb3JtdWxhLA0KICBkYXRhID0gdHJhaW4uZGF0YS5jbHMsDQogIGhpZGRlbiA9IGMoYmVzdC4ybGF5ZXIkaGlkZGVuMSwgYmVzdC4ybGF5ZXIkaGlkZGVuMiksDQogIGxpbmVhci5vdXRwdXQgPSBGQUxTRSwNCiAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsDQogIHN0ZXBtYXggPSAxZTYNCikNCiMjDQojcGxvdChubi4ybGF5ZXIpDQpgYGANCg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3R3by1oaWRkZW4tTUxQLURpYWJldGVzLnBuZyIpDQpgYGANCg0KDQpJbiB0aGUgdHdvIG1vZGVsIHBsb3RzIGFib3ZlLCB0aGUgKipFcnJvcioqIGFuZCAqKlN0ZXBzKiogdmFsdWVzIGRpc3BsYXllZCBhdCB0aGUgYm90dG9tIHJlcHJlc2VudDoNCg0KKiAqKlN0ZXBzKio6IFRoZSBudW1iZXIgb2YgKip0cmFpbmluZyBpdGVyYXRpb25zKiogKGVwb2NocykgY29tcGxldGVkIGR1cmluZyBtb2RlbCBvcHRpbWl6YXRpb24uIEVhY2ggc3RlcCBjb3JyZXNwb25kcyB0byBvbmUgY29tcGxldGUgZm9yd2FyZC9iYWNrd2FyZCBwYXNzIGFuZCB3ZWlnaHQgdXBkYXRlIGN5Y2xlLg0KDQoqICoqRXJyb3JzKio6IFRoZSAqKnRyYWluaW5nIGVycm9yKiogcmVmbGVjdHMgdGhlIGxvc3MgZnVuY3Rpb24gdmFsdWUgKHR5cGljYWxseSBTU0UgZm9yIHJlZ3Jlc3Npb24gb3IgY3Jvc3MtZW50cm9weSBmb3IgY2xhc3NpZmljYXRpb24pLiBUaGUgZGlzcGxheWVkICoqRXJyb3IqKiByZXByZXNlbnRzIHRoZSBmaW5hbCBlcnJvciB2YWx1ZSBhY2hpZXZlZCB3aGVuIHRoZSBvcHRpbWl6YXRpb24gcHJvY2VzcyBjb252ZXJnZXMuDQoNCg0KTmV4dCwgd2Ugd3JpdGUgYSBjdXN0b20gZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyB0byBhc3Nlc3MgdGhlIGdsb2JhbCBwZXJmb3JtYW5jZSB0aHJvdWdoIFJPQyBjdXJ2ZXMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGFyZWFzIHVuZGVyIHRoZSBST0MgY3VydmVzLg0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gdG8gZXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2UNCmV2YWx1YXRlLm1vZGVsIDwtIGZ1bmN0aW9uKG1vZGVsLCB0ZXN0LmRhdGEpIHsNCiAgIyBNYWtlIHByZWRpY3Rpb25zDQogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIHRlc3QuZGF0YSkNCiAgcHJlZGljdGVkLmNsYXNzZXMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID4gMC41LCAxLCAwKQ0KICANCiAgIyBDYWxjdWxhdGUgbWV0cmljcw0KICBhY2N1cmFjeSA8LSBtZWFuKHByZWRpY3RlZC5jbGFzc2VzID09IHRlc3QuZGF0YSRkaWFiZXRlcykNCiAgY29uZnVzaW9uLm1hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0ZWQuY2xhc3NlcywgQWN0dWFsID0gdGVzdC5kYXRhJGRpYWJldGVzKQ0KICByb2Mub2JqIDwtIHJvYyh0ZXN0LmRhdGEkZGlhYmV0ZXMsIHByZWRpY3Rpb25zKQ0KICBhdWMudmFsdWUgPC0gYXVjKHJvYy5vYmopDQogIA0KICByZXR1cm4obGlzdCgNCiAgICBhY2N1cmFjeSA9IGFjY3VyYWN5LA0KICAgIGNvbmZ1c2lvbi5tYXRyaXggPSBjb25mdXNpb24ubWF0cml4LA0KICAgIHJvYy5vYmogPSByb2Mub2JqLA0KICAgIGF1YyA9IGF1Yy52YWx1ZQ0KICApKQ0KfQ0KDQojIEV2YWx1YXRlIHNpbmdsZSBoaWRkZW4gbGF5ZXIgbW9kZWwNCnBlcmYuMWxheWVyIDwtIGV2YWx1YXRlLm1vZGVsKG5uLjFsYXllciwgdGVzdC5kYXRhLmNscykNCiNwcmludChwZXJmLjFsYXllcltjKCJhY2N1cmFjeSIsICJjb25mdXNpb25fbWF0cml4IiwgImF1YyIpXSkNCg0KIyBFdmFsdWF0ZSB0d28gaGlkZGVuIGxheWVycyBtb2RlbA0KcGVyZi4ybGF5ZXIgPC0gZXZhbHVhdGUubW9kZWwobm4uMmxheWVyLCB0ZXN0LmRhdGEuY2xzKQ0KI3ByaW50KHBlcmYuMmxheWVyW2MoImFjY3VyYWN5IiwgImNvbmZ1c2lvbl9tYXRyaXgiLCAiYXVjIildKQ0KYGBgDQoNCg0KV2UgdXNlIGNsYXNzaWMgbG9naXN0aWMgcmVncmVzc2lvbiBhcyB0aGUgYmFzZSBtb2RlbCBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSB0d28gTUxQcyB1c2luZyBST0MgY3VydmVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIEFVQyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuDQoNCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9DQojIFRyYWluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgKGJhc2UgbW9kZWwpDQpsb2dpdC5tb2RlbCA8LSBnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW4uZGF0YS5jbHMsIGZhbWlseSA9IGJpbm9taWFsKQ0KDQojIEV2YWx1YXRlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmxvZ2l0LnByZWQgPC0gcHJlZGljdChsb2dpdC5tb2RlbCwgdGVzdC5kYXRhLmNscywgdHlwZSA9ICJyZXNwb25zZSIpDQpsb2dpdC5jbGFzc2VzIDwtIGlmZWxzZShsb2dpdC5wcmVkID4gMC41LCAxLCAwKQ0KbG9naXQuYWNjdXJhY3kgPC0gbWVhbihsb2dpdC5jbGFzc2VzID09IHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMpDQpsb2dpdC5yb2MgPC0gcm9jKHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMsIGxvZ2l0LnByZWQpDQpsb2dpdC5hdWMgPC0gYXVjKGxvZ2l0LnJvYykNCg0KIyMNCnJvYy4xbGF5ZXIgPC0gcGVyZi4xbGF5ZXIkcm9jLm9iag0Kcm9jLjJsYXllciA8LSBwZXJmLjJsYXllciRyb2Mub2JqDQpyb2MubG9naXQgPC0gbG9naXQucm9jDQoNCiMjIHNwZWNpZmljaXR5IGFuZCBzZW5zaXRpdml0eQ0Kc2VuLjFsYXllciA8LSByb2MuMWxheWVyJHNlbnNpdGl2aXRpZXMNCnNwZS4xbGF5ZXIgPC0gcm9jLjFsYXllciRzcGVjaWZpY2l0aWVzDQpzZW4uMmxheWVyIDwtIHJvYy4ybGF5ZXIkc2Vuc2l0aXZpdGllcw0Kc3BlLjJsYXllciA8LSByb2MuMmxheWVyJHNwZWNpZmljaXRpZXMNCnNlbi5sb2dpdCA8LSByb2MubG9naXQkc2Vuc2l0aXZpdGllcw0Kc3BlLmxvZ2l0IDwtIHJvYy5sb2dpdCRzcGVjaWZpY2l0aWVzDQoNCiMjIEFVQw0KYXVjLjFsYXllciA8LSByb2MuMWxheWVyJGF1Yw0KYXVjLjJsYXllciA8LSByb2MuMmxheWVyJGF1Yw0KYXVjLmxvZ2l0IDwtIHJvYy5sb2dpdCRhdWMNCg0KIyMgUGxvdCBST0MgY3VydmVzIGZvciBjb21wYXJpc29uDQpwYXIocHR5ID0gInMiKSAgICMgbWFrZSBhIHNxdWFyZSBwbG90IHRvIGF2YW9pZCBkaXN0b3J0aW9uDQpwbG90KDEtc3BlLjFsYXllciwgc2VuLjFsYXllciwgdHlwZSA9ICJsIiwgbHR5ID0gMSwNCiAgICAgY29sID0gImJsdWUiLCANCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLA0KICAgICB5bGFiID0gInNlbnNpdHZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZSBDb21wYXJpc29uIikNCg0KbGluZXMoMS1zcGUuMmxheWVyLCBzZW4uMmxheWVyLCBsdHkgPSAxLCBjb2wgPSAiZGFya3JlZCIpDQpsaW5lcygxLXNwZS5sb2dpdCwgc2VuLmxvZ2l0LCBsdHkgPSAxLCBjb2wgPSAiZGFya2dyZWVuIikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCANCiAgICAgICBsZWdlbmQgPSBjKHBhc3RlKCIxLWxheWVyIE1MUCAoQVVDID0iLCByb3VuZChwZXJmLjFsYXllciRhdWMsIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgcGFzdGUoIjItbGF5ZXIgTUxQIChBVUMgPSIsIHJvdW5kKHBlcmYuMmxheWVyJGF1YywgMyksICIpIiksDQogICAgICAgICAgICAgICAgICBwYXN0ZSgiTG9naXN0aWMgUmVnIChBVUMgPSIsIHJvdW5kKGxvZ2l0LmF1YywgMyksICIpIikpLA0KICAgICAgICAgICAgICAgIGNvbCA9IGMoImJsdWUiLCAiZGFya3JlZCIsICJkYXJrZ3JlZW4iKSwgDQogICAgICAgICAgICAgICAgbHR5ID0gMSwgY2V4ID0gMC43LCBidHkgPSAibiIpDQpgYGANCg0KVGhlIFJPQyBhbmFseXNpcyBzaG93cyB0aGF0IHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgcGVyZm9ybXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gdGhlIHR3byBNTFBzLiBUaGUgdHdvIE1MUHMgcGVyZm9ybSBlcXVhbGx5IHdlbGwsIGV2ZW4gdGhvdWdoIHRoZSB0d28taGlkZGVuLWxheWVyIE1MUCBoYXMgbW9yZSBwYXJhbWV0ZXJzICh3ZWlnaHRzKSBhbmQgaXMgbW9yZSBjb21wbGV4IHRoYW4gdGhlIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQLiBJbiBnZW5lcmFsLCBhIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQIGlzIHJlY29tbWVuZGVkIGlmIGFuIE1MUCBpcyB1c2VkIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KDQoNCiMgTUxQIG9yIENsYXNzaWNhbCBNb2RlbHM/DQoNCg0KV2UgaGF2ZSBkaXNjdXNzZWQgdGhlIE1MUCBhcmNoaXRlY3R1cmUgYW5kIHRoZSBwcm9jZXNzIG9mIGltcGxlbWVudGluZyBNTFBzIGFsb25nc2lkZSBjbGFzc2ljIG1vZGVscy4gQ2FzZSBzdHVkaWVzIHVzaW5nIHRoZSAqKkJvc3RvbiBIb3VzaW5nIERhdGEqKiBkZW1vbnN0cmF0ZSB0aGF0IE1MUCByZWdyZXNzaW9uIG91dHBlcmZvcm1zIGNsYXNzaWMgbGluZWFyIHJlZ3Jlc3Npb24sIHdoaWxlIHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcGVyZm9ybXMgYmV0dGVyIHRoYW4gTUxQIGNsYXNzaWZpY2F0aW9uIGluIHRoZSAqKlBpbWEgSW5kaWFuIERpYWJldGVzKiogZGF0YXNldCBhbmFseXNpcy4gTmV4dCwgd2Ugd2lsbCBwcmVzZW50IGEgZ2VuZXJhbCBjb21wYXJpc29uIGJldHdlZW4gdGhlc2UgbW9kZWxzLg0KDQpcDQoNCioqTUxQIENsYXNzaWZpY2F0aW9uIHZzLiBMb2dpc3RpYyBSZWdyZXNzaW9uKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiBtdWx0aWxheWVyIHBlcmNlcHRyb24gKE1MUCkgY2xhc3NpZmllcnMgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaGlnaGxpZ2h0cyBhIGZ1bmRhbWVudGFsIHRyYWRlLW9mZiBiZXR3ZWVuIGZsZXhpYmlsaXR5IGFuZCBzaW1wbGljaXR5LiANCg0KKiBMb2dpc3RpYyByZWdyZXNzaW9uLCBhcyBhIGxpbmVhciBjbGFzc2lmaWVyLCBwZXJmb3JtcyB3ZWxsIHdoZW4gZGVjaXNpb24gYm91bmRhcmllcyBhcmUgYXBwcm94aW1hdGVseSBsaW5lYXIsIG9mZmVyaW5nIGhpZ2ggaW50ZXJwcmV0YWJpbGl0eSwgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LCBhbmQgcm9idXN0bmVzcyB0byBvdmVyZml0dGluZ+KAlHBhcnRpY3VsYXJseSB3aXRoIGxpbWl0ZWQgZGF0YS4gDQoNCiogTUxQcywgd2l0aCB0aGVpciBub25saW5lYXIgYWN0aXZhdGlvbiBmdW5jdGlvbnMsIGNhbiBtb2RlbCBtb3JlIGNvbXBsZXggZGVjaXNpb24gYm91bmRhcmllcyBidXQgcmVxdWlyZSBjYXJlZnVsIHR1bmluZyBvZiBhcmNoaXRlY3R1cmUgYW5kIHJlZ3VsYXJpemF0aW9uIHRvIGF2b2lkIG92ZXJmaXR0aW5nLiANCg0KKiBUaGUgYWRkZWQgY29tcGxleGl0eSBvZiBuZXVyYWwgbmV0d29ya3MgbWF5IG5vdCBhbHdheXMgYmUganVzdGlmaWVkIGZvciBzaW1wbGVyIGNsYXNzaWZpY2F0aW9uIHRhc2tzLiBIb3dldmVyLCBNTFBzIHJlbWFpbiB2YWx1YWJsZSB3aGVuIGRlYWxpbmcgd2l0aCBoaWdobHkgbm9ubGluZWFyIGRhdGEgd2hlcmUgbG9naXN0aWMgcmVncmVzc2lvbuKAmXMgbGluZWFyIGFzc3VtcHRpb25zIGZhaWwuDQoNClwNCg0KKipNTFAgUmVncmVzc2lvbiB2cy4gQ2xhc3NpYyBMaW5lYXIgUmVncmVzc2lvbioqDQoNClNpbWlsYXIgdG8gdGhlaXIgY2xhc3NpZmljYXRpb24gY291bnRlcnBhcnRzLCBNTFAgcmVncmVzc2lvbiBtb2RlbHMgYW5kIGNsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBzZXJ2ZSBkaWZmZXJlbnQgcHVycG9zZXMgZGVwZW5kaW5nIG9uIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuIA0KDQoqIExpbmVhciByZWdyZXNzaW9uIGlzIG9wdGltYWwgd2hlbiByZWxhdGlvbnNoaXBzIGJldHdlZW4gcHJlZGljdG9ycyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSBhcmUgbGluZWFyLCBwcm92aWRpbmcgaW50ZXJwcmV0YWJsZSBjb2VmZmljaWVudHMsIGZhc3QgdHJhaW5pbmcsIGFuZCBsb3cgcmlzayBvZiBvdmVyZml0dGluZy4gDQoNCiogTUxQIHJlZ3Jlc3Npb24sIGhvd2V2ZXIsIGV4Y2VscyBpbiBjYXB0dXJpbmcgbm9ubGluZWFyIGFuZCBpbnRlcmFjdGl2ZSBlZmZlY3RzLCBtYWtpbmcgaXQgc3VpdGFibGUgZm9yIG1vcmUgY29tcGxleCByZWdyZXNzaW9uIHRhc2tzIHdoZXJlIGxpbmVhciBtb2RlbHMgdW5kZXJwZXJmb3JtLiANCg0KKiBUaGUgdHJhZGUtb2ZmIGxpZXMgaW4gY29tcHV0YXRpb25hbCBjb3N0LCB0dW5pbmcgY29tcGxleGl0eSwgYW5kIHRoZSBuZWVkIGZvciBsYXJnZXIgZGF0YXNldHMgdG8gcHJldmVudCAqKm92ZXJmaXR0aW5nKiouIElmIHRoZSB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlIGlzIHVua25vd24sIGEgcHJhY3RpY2FsIGFwcHJvYWNoIGlzIHRvIGJlZ2luIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24gYXMgYSBiYXNlbGluZSBiZWZvcmUgY29uc2lkZXJpbmcgTUxQcyBpZiByZXNpZHVhbHMgc3VnZ2VzdCB1bm1vZGVsZWQgbm9ubGluZWFyaXR5Lg0KDQpcDQoNCioqU2luZ2xlLUhpZGRlbi1MYXllciB2cy4gVHdvLUhpZGRlbi1MYXllciBNTFBzKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiAqKnNpbmdsZS0qKiBhbmQgKip0d28taGlkZGVuLWxheWVyIE1MUHMqKiB1bmRlcnNjb3JlcyBtb2RlbCBjb21wbGV4aXR5LiANCg0KKiAqKkRlZXBlciBuZXR3b3JrcyoqIHRoZW9yZXRpY2FsbHkgaGF2ZSBncmVhdGVyIHJlcHJlc2VudGF0aW9uYWwgcG93ZXIuIENhc2Ugc3R1ZGllcyBmb3VuZCB0aGF0IHRoZSAqKnR3by1oaWRkZW4tbGF5ZXIqKiBNTFAgZGlkIG5vdCBvdXRwZXJmb3JtIGl0cyBzaW1wbGVyIGNvdW50ZXJwYXJ0LCBkZXNwaXRlIGhhdmluZyBtb3JlIHBhcmFtZXRlcnMuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHRoZSB1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtLCB3aGljaCBzdGF0ZXMgdGhhdCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgKHdpdGggc3VmZmljaWVudCBuZXVyb25zKSBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24uIA0KDQoqIFRoZSBhZGRpdGlvbmFsIGxheWVyIGludHJvZHVjZWQgdW5uZWNlc3NhcnkgY29tcGxleGl0eSB3aXRob3V0IGdhaW5zIGluIGFjY3VyYWN5LCByZWluZm9yY2luZyB0aGF0IGRlZXBlciBhcmNoaXRlY3R1cmVzIHNob3VsZCBvbmx5IGJlIGV4cGxvcmVkIHdoZW4gc2ltcGxlciBtb2RlbHMgcHJvdmUgaW5hZGVxdWF0ZeKAlHR5cGljYWxseSBpbiBoaWdobHkgbm9ubGluZWFyIG9yIGhpZ2gtZGltZW5zaW9uYWwgcHJvYmxlbXMuDQoNClwNCg0KKipHZW5lcmFsIFJlY29tbWVuZGF0aW9ucyoqDQoNCiAgKyAqKlN0YXJ0IHNpbXBsZSoqOiBBbHdheXMgYmVnaW4gd2l0aCBjbGFzc2ljYWwgbW9kZWxzIChsaW5lYXIvbG9naXN0aWMgcmVncmVzc2lvbikgYXMgYmFzZWxpbmVzLiBUaGVpciBpbnRlcnByZXRhYmlsaXR5IGFuZCBlZmZpY2llbmN5IG1ha2UgdGhlbSBpZGVhbCBmb3IgaW5pdGlhbCBleHBsb3JhdGlvbi4NCg0KICArICoqVXNlIE1MUHMganVkaWNpb3VzbHkqKjogUmVzZXJ2ZSBNTFBzIGZvciBjYXNlcyB3aGVyZSBsaW5lYXIgbW9kZWxzIHVuZGVycGVyZm9ybSBkdWUgdG8gY2xlYXIgbm9ubGluZWFyaXR5IG9yIGludGVyYWN0aW9uIGVmZmVjdHMuIFByZWZlciBzaW5nbGUtaGlkZGVuLWxheWVyIGFyY2hpdGVjdHVyZXMgdW5sZXNzIGRlZXBlciBuZXR3b3JrcyBkZW1vbnN0cmFibHkgaW1wcm92ZSBwZXJmb3JtYW5jZS4NCg0KICArICoqQmFsYW5jZSBjb21wbGV4aXR5IGFuZCBwZXJmb3JtYW5jZSoqOiBBdm9pZCB1bm5lY2Vzc2FyeSBtb2RlbCBjb21wbGV4aXR54oCUZmF2b3IgdGhlIHNpbXBsZXN0IG1vZGVsIHRoYXQgYWNoaWV2ZXMgc2F0aXNmYWN0b3J5IGFjY3VyYWN5Lg0KDQogICsgKipIeWJyaWQgYXBwcm9hY2gqKjogQ29tYmluZSBsaW5lYXIgbW9kZWxzIHdpdGggZmVhdHVyZSBlbmdpbmVlcmluZyBvciBrZXJuZWwgbWV0aG9kcyBiZWZvcmUgcmVzb3J0aW5nIHRvIE1MUHMsIHBhcnRpY3VsYXJseSBpbiByZXNvdXJjZS1jb25zdHJhaW5lZCBzZXR0aW5ncy4NCg0KDQoNClVsdGltYXRlbHksIG1vZGVsIGNob2ljZSBzaG91bGQgYmUgZ3VpZGVkIGJ5IHByb2JsZW0gY29uc3RyYWludHMsIGRhdGEgc3RydWN0dXJlLCBhbmQgdGhlIHRyYWRlLW9mZnMgYmV0d2VlbiBhY2N1cmFjeSwgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGNvbXB1dGF0aW9uYWwgY29zdC4gQSBzeXN0ZW1hdGljLCBpdGVyYXRpdmUgYXBwcm9hY2jigJRmcm9tIGxpbmVhciBtb2RlbHMgdG8gc2hhbGxvdyB0aGVuIGRlZXAgbmV0d29ya3PigJRlbnN1cmVzIGJvdGggZWZmaWNpZW5jeSBhbmQgcGVyZm9ybWFuY2UuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=